summaryrefslogtreecommitdiffstats
path: root/src/render.py
blob: 2fedf7dcf2a042450c1d7928f9c1e2cdaa2c56b3 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
from typing import Dict

from util import parse_param, to_camel_case, to_snake_case, SCALAR_MAPPING
from entity import Field, Struct, Enum, Method, Type, Scalar


def render(parsed, entities, log):
    structs: Dict[str, Type] = dict()
    enums: Dict[str, Enum] = dict()
    methods: Dict[str, Method] = dict()

    for (scalar, _) in SCALAR_MAPPING.values():
        structs.update({scalar: Scalar(name=scalar)})

    def parse_decl(decl) -> tuple: # -> (description, ctor name, fields, base name)
        docs, derived, params, base = decl.children
        docs = {
            doc.children[0]: ''.join(doc.children[1].children)
            for doc in docs.children
        }
        params = params.children
        fields = []
        for param in params:
            name, raw_type = param.children
            typ, inner_typ, deserializer = parse_param(raw_type)
            doc = docs[name]
            if name == 'description':
                doc = docs['param_description']
            fields.append(Field(typeid=typ, name=str(name), deserializer=deserializer, doc=doc, inner_type=inner_typ))
        return docs['description'], str(derived), fields, str(base)
    
    def find_type(name: str) -> Type:
            if name in structs:
                return structs[name]
            elif name in enums:
                return enums[name]
            else:
                raise ValueError(f"could not find type {name}")


    parsed_decls, parsed_methods = parsed.children

    for decl in parsed_decls.children:
        if decl.data == 'decl':
            description, type_name, fields, base_type = parse_decl(decl)
            rusty_type_name = to_camel_case(type_name)
            rusty_base_type = to_camel_case(base_type)


            struct = Struct(name=rusty_type_name, doc=description)
            structs.update({rusty_type_name: struct})
            for field in fields:
                struct.add_field(field)

            if rusty_type_name != rusty_base_type:
                enums[to_camel_case(rusty_base_type)].add_member(struct)

        else:
            name, doc = decl.children
            rusty_name = to_camel_case(name)
            doc = ''.join(doc.children[1].children)
            enums[rusty_name] = Enum(name=rusty_name, doc=doc)


    for decl in parsed_methods.children:
        docs, name, params, ret = parse_decl(decl)
        ret_typ = find_type(to_camel_case(ret))
        m = Method(orig_name=name, ret=ret_typ, docs=docs)
        for param in params:
            param.initialize_with_type(find_type(param.inner_type))
            m.add_param(param)

        methods[m.name] = m


    for struct in structs.values():
        if isinstance(struct, Scalar):
            continue
        for field in struct.deps:
            field.initialize_with_type(find_type(field.inner_type))


    for e_name, enum in enums.items():
        if e_name not in entities:
            enum.exclude = True
        else:
            for member in enum.deps:
                if len(member.deps) == 0:
                    member.exclude = True
                else:
                    entities.append(member.name) # force inclusion of children
    
    for s_name, struct in structs.items():
        if s_name not in entities:
            struct.exclude = True

    prelude = '''
        #![allow(unused)]
        use serde::Deserializer;
        use tdlib_rs::client::ClientLike;

        use serde_derive::{Serialize, Deserialize};
        use serde_json::{json, Value as SerdeJsonValue};
        use tdlib_rs::Client;
        use tdlib_rs::client::ResponseFuture;
        use super::{deserialize_i64_0, deserialize_i64_1};
    '''
    print(prelude)
    
    for s_name, struct in structs.items():
        if not struct.is_excluded and s_name in entities:
            struct.render_as_decl()

    for e_name, enum in enums.items():
        if not enum.is_excluded and e_name in entities:
            enum.render_as_decl()

    print('pub trait ClientExt: ClientLike {')
    for m_name, meth in methods.items():
        if m_name in entities:
            meth.render_as_decl()
    print('}')