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
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
|
from __future__ import annotations
from typing import Optional, List
from dataclasses import dataclass
from sys import stderr
import util
@dataclass
class Field:
name: str
typeid: str # full type, e.g. Vec<i32>
doc: str
inner_type: str # inner type, e.g. if typeid is 'Vec<i32>', then inner_type is 'i32'
deserializer: Optional[int] = None # depth of vectorness of i64
typ: Optional[Type] = None
rename: Optional[str] = None
def __post_init__(self):
self.maybe_enable_optional()
self.doc = util.escape_doc(self.doc)
if self.name == 'type':
self.name = 'type_'
self.rename = 'type'
def maybe_enable_optional(self):
optional_heuristics = {
'may be null',
'only available to bots',
'bots only',
'or null'
}
for s in optional_heuristics:
if s in self.doc:
if self.deserializer is not None:
raise ValueError("i64 cannot be optional")
self.typeid = f"Option<{self.typeid}>"
break
def initialize_with_type(self, typ: Type):
self.typ = typ
class Type:
def __init__(self, name: str, inner_doc: Optional[str]=None):
self.name = name
self.doc = util.escape_doc(inner_doc or '')
self.exclude = False
@property
def is_excluded(self) -> bool:
return self.exclude
def render_as_decl(self):
'''
Fully declare self, e.g.
#[doc = ...] // for everything
#[derive([De]Serialize)] // for types and classes only
#[serde(tag = "@type")] // for classes only
#[serde(rename_all = "camelCase)] // classes only
'''
raise NotImplementedError()
def render_as_param(self, param_name: str):
'''
Render self as param, e.g.
param_name: self.name
'''
raise NotImplementedError()
def render_as_field(self, field_name: str):
'''
Render self as field, e.g.
#[doc = ...] // for types only
#[serde(rename = ...)] // for types and classes
#[serde(deserialize_with = ...)] // for types only
field_name: self.name,
'''
raise NotImplementedError()
def render_as_variant(self):
'''
Just print `self.name(self.name),`
'''
raise NotImplementedError()
def rusty_name(self) -> str:
'''
Sometimes, name should convert case for rust
'''
raise NotImplementedError()
class Enum(Type):
def __init__(self, name, doc):
Type.__init__(self, name, doc)
self.deps = list()
def render_as_decl(self):
print(f'#[doc = "{self.doc}"]')
print('#[derive(Serialize, Deserialize, Clone, Debug)]')
print(f"pub enum {self.rusty_name()} {{")
for dep in self.deps:
if len(dep.deps) > 0:
print(f"{dep.name}({dep.name}),")
else:
print(f"{dep.name},")
print(f"}}")
def render_as_param(self, param_name: str):
print(f"{param_name}: {self.rusty_name()},")
def render_as_field(self, field_name: str):
print(f'#[serde(rename = {self.name})]')
print(f'inline serde rename for {self.name}', file=stderr)
print(f"{field_name}: {self.rusty_name()},")
def rusty_name(self) -> str:
return self.name[0].upper() + self.name[1:]
def add_member(self, member: Struct):
self.deps.append(member)
@property
def is_excluded(self):
return self.exclude or self.name in util.ENUM_EXCLUDE_ALWAYS
class Struct(Type):
def __init__(self, name: str, doc: str, deserializer: Optional[str]=None):
Type.__init__(self, name, doc)
self.deserializer = deserializer
self.deps: List[Field] = list()
def render_as_decl(self):
print(f'#[doc = "{self.doc}"]')
print('#[derive(Serialize, Deserialize, Clone, Debug)]')
print(f'pub struct {self.rusty_name()} {{')
for field in self.deps:
print(f'#[doc = "{field.doc}"]')
if field.rename is not None:
print(f"#[serde(rename = \"{field.rename}\")]")
if field.deserializer is not None and not field.typ.is_excluded:
print(f'#[serde(deserialize_with="deserialize_i64_{field.deserializer}")]')
if field.typ.is_excluded:
print(f'pub {field.name}: SerdeJsonValue,')
else:
print(f'pub {field.name}: {field.typeid},')
print(f'}}')
def render_as_variant(self):
print('//rename?')
print(f'{self.name}({self.name}),')
def rusty_name(self) -> str:
return self.name[0].upper() + self.name[1:]
def add_field(self, field: Field):
self.deps.append(field)
@property
def is_excluded(self):
return self.exclude or self.name in util.STRUCT_EXCLUDE_ALWAYS
class Scalar(Type):
def render_as_decl(self):
pass
def render_as_variant(self):
raise TypeError("Attempt to use scalar as variant")
def rusty_name(self):
return util.convert_scalar_name(self.name)
def add_field(self, name: str, doc: str, typ: Type):
raise TypeError("Attempt to add dependency to scalar")
@property
def is_excluded(self):
return False
class Method:
def __init__(self, orig_name: str, ret: Type, docs: str):
self.name = util.to_snake_case(orig_name)
self.orig_name = orig_name
self.params: List[Field] = list()
self.ret = ret
self.doc = util.escape_doc(docs)
def add_param(self, field: Field):
self.params.append(field)
def render_as_decl(self):
actual_ret = 'SerdeJsonValue' if self.ret.is_excluded else self.ret.name
print(f'#[doc="{self.doc}"]')
if len(self.params) > 0:
print(f'#[doc=" \\n\\n"]') # ensure newline
print(f'#[doc="parameters: "]')
for param in self.params:
print(f'#[doc=" * `{param.name}`: {param.doc}"]')
if self.ret.is_excluded:
print(f'#[doc=" \\n\\n"]') # ensure newline
print(f'#[doc="Return type: `{self.ret.name}`"]')
print(f'fn {self.name}(&self,')
for param in self.params:
typeid = 'SerdeJsonValue' if param.typ.is_excluded else param.typeid
print(f'{param.name}: {typeid},')
print(f') -> ResponseFuture<{actual_ret}> {{')
print(f'self.send(json!({{')
for param in self.params:
name = param.rename or param.name
print(f'"{param.name}": {name},')
print(f'"@type": "{self.orig_name}"')
print(f'}}))')
print(f'}}')
|