1use std::collections::HashMap;
2use std::fmt::Write;
3use trans_gen_core::trans_schema::*;
4use trans_gen_core::Writer;
5
6fn conv(name: &str) -> String {
7 name.replace("Int32", "Int")
8 .replace("Int64", "Long")
9 .replace("Float32", "Float")
10 .replace("Float64", "Double")
11}
12
13pub struct Generator {
14 model_init: String,
15 files: HashMap<String, String>,
16}
17
18fn write_struct(
19 writer: &mut Writer,
20 struc: &Struct,
21 base: Option<(&Name, usize)>,
22) -> std::fmt::Result {
23 for field in &struc.fields {
25 fn add_imports(writer: &mut Writer, schema: &Schema) -> std::fmt::Result {
26 match schema {
27 Schema::Struct(Struct { name, .. })
28 | Schema::OneOf {
29 base_name: name, ..
30 }
31 | Schema::Enum {
32 base_name: name, ..
33 } => {
34 writeln!(
35 writer,
36 "from .{} import {}",
37 name.snake_case(conv),
38 name.camel_case(conv)
39 )?;
40 }
41 Schema::Option(inner) => {
42 add_imports(writer, inner)?;
43 }
44 Schema::Vec(inner) => {
45 add_imports(writer, inner)?;
46 }
47 Schema::Map(key_type, value_type) => {
48 add_imports(writer, key_type)?;
49 add_imports(writer, value_type)?;
50 }
51 Schema::Bool
52 | Schema::Int32
53 | Schema::Int64
54 | Schema::Float32
55 | Schema::Float64
56 | Schema::String => {}
57 }
58 Ok(())
59 }
60 add_imports(writer, &field.schema)?;
61 }
62
63 write!(writer, "class {}", struc.name.camel_case(conv))?;
65 if let Some((base_name, _)) = base {
66 write!(writer, "({})", base_name.camel_case(conv))?;
67 }
68 writeln!(writer, ":")?;
69 writer.inc_ident();
70 if let Some((_, discriminant)) = base {
71 writeln!(writer, "TAG = {}", discriminant)?;
72 }
73
74 write!(writer, "def __init__(self")?;
76 for field in &struc.fields {
77 write!(writer, ", {}", field.name.snake_case(conv))?;
78 }
79 writeln!(writer, "):")?;
80 for field in &struc.fields {
81 writeln!(
82 writer,
83 " self.{} = {}",
84 field.name.snake_case(conv),
85 field.name.snake_case(conv)
86 )?;
87 }
88 if struc.fields.is_empty() {
89 writeln!(writer, " pass")?;
90 }
91
92 writeln!(writer, "@staticmethod")?;
94 writeln!(writer, "def read_from(stream):")?;
95 writer.inc_ident();
96 for field in &struc.fields {
97 fn assign(writer: &mut Writer, to: &str, schema: &Schema) -> std::fmt::Result {
98 match schema {
99 Schema::Bool => {
100 writeln!(writer, "{} = stream.read_bool()", to)?;
101 }
102 Schema::Int32 => {
103 writeln!(writer, "{} = stream.read_int()", to)?;
104 }
105 Schema::Int64 => {
106 writeln!(writer, "{} = stream.read_long()", to)?;
107 }
108 Schema::Float32 => {
109 writeln!(writer, "{} = stream.read_float()", to)?;
110 }
111 Schema::Float64 => {
112 writeln!(writer, "{} = stream.read_double()", to)?;
113 }
114 Schema::String => {
115 writeln!(writer, "{} = stream.read_string()", to)?;
116 }
117 Schema::Struct(Struct { name, .. })
118 | Schema::OneOf {
119 base_name: name, ..
120 } => {
121 writeln!(
122 writer,
123 "{} = {}.read_from(stream)",
124 to,
125 name.camel_case(conv)
126 )?;
127 }
128 Schema::Option(inner) => {
129 writeln!(writer, "if stream.read_bool():")?;
130 writer.inc_ident();
131 assign(writer, to, inner)?;
132 writer.dec_ident();
133 writeln!(writer, "else:")?;
134 writeln!(writer, " {} = None", to)?;
135 }
136 Schema::Vec(inner) => {
137 writeln!(writer, "{} = []", to)?;
138 writeln!(writer, "for _ in range(stream.read_int()):")?;
139 writer.inc_ident();
140 assign(writer, &format!("{}_element", to), inner)?;
141 writeln!(writer, "{}.append({}_element)", to, to)?;
142 writer.dec_ident();
143 }
144 Schema::Map(key_type, value_type) => {
145 writeln!(writer, "{} = {{}}", to)?;
146 writeln!(writer, "for _ in range(stream.read_int()):")?;
147 writer.inc_ident();
148 assign(writer, &format!("{}_key", to), key_type)?;
149 assign(writer, &format!("{}_value", to), value_type)?;
150 writeln!(writer, "{}[{}_key] = {}_value", to, to, to)?;
151 writer.dec_ident();
152 }
153 Schema::Enum { base_name, .. } => {
154 writeln!(
155 writer,
156 "{} = {}(stream.read_int())",
157 to,
158 base_name.camel_case(conv)
159 )?;
160 }
161 }
162 Ok(())
163 }
164 assign(writer, &field.name.snake_case(conv), &field.schema)?;
165 }
166 write!(writer, "return {}(", struc.name.camel_case(conv))?;
167 let mut first = true;
168 for field in &struc.fields {
169 if first {
170 first = false;
171 } else {
172 write!(writer, ", ")?;
173 }
174 write!(writer, "{}", field.name.snake_case(conv))?;
175 }
176 writeln!(writer, ")")?;
177 writer.dec_ident();
178
179 writeln!(writer, "def write_to(self, stream):")?;
181 writer.inc_ident();
182 if base.is_some() {
183 writeln!(writer, "stream.write_int(self.TAG)")?;
184 } else if struc.fields.is_empty() && struc.magic.is_none() {
185 writeln!(writer, "pass")?;
186 }
187 if let Some(magic) = struc.magic {
188 writeln!(writer, "stream.write_int({})", magic)?;
189 }
190 for field in &struc.fields {
191 fn write(writer: &mut Writer, value: &str, schema: &Schema) -> std::fmt::Result {
192 match schema {
193 Schema::Bool => {
194 writeln!(writer, "stream.write_bool({})", value)?;
195 }
196 Schema::Int32 => {
197 writeln!(writer, "stream.write_int({})", value)?;
198 }
199 Schema::Int64 => {
200 writeln!(writer, "stream.write_long({})", value)?;
201 }
202 Schema::Float32 => {
203 writeln!(writer, "stream.write_float({})", value)?;
204 }
205 Schema::Float64 => {
206 writeln!(writer, "stream.write_double({})", value)?;
207 }
208 Schema::String => {
209 writeln!(writer, "stream.write_string({})", value)?;
210 }
211 Schema::Struct(_) | Schema::OneOf { .. } => {
212 writeln!(writer, "{}.write_to(stream)", value)?;
213 }
214 Schema::Option(inner) => {
215 writeln!(writer, "if {} is None:", value)?;
216 writeln!(writer, " stream.write_bool(False)")?;
217 writeln!(writer, "else:")?;
218 writer.inc_ident();
219 writeln!(writer, "stream.write_bool(True)")?;
220 write(writer, value, inner)?;
221 writer.dec_ident();
222 }
223 Schema::Vec(inner) => {
224 writeln!(writer, "stream.write_int(len({}))", value)?;
225 writeln!(writer, "for element in {}:", value)?;
226 writer.inc_ident();
227 write(writer, "element", inner)?;
228 writer.dec_ident();
229 }
230 Schema::Map(key_type, value_type) => {
231 writeln!(writer, "stream.write_int(len({}))", value)?;
232 writeln!(writer, "for key, value in {}.items():", value)?;
233 writer.inc_ident();
234 write(writer, "key", key_type)?;
235 write(writer, "value", value_type)?;
236 writer.dec_ident();
237 }
238 Schema::Enum { .. } => {
239 writeln!(writer, "stream.write_int({})", value)?;
240 }
241 }
242 Ok(())
243 }
244 write(
245 writer,
246 &format!("self.{}", field.name.snake_case(conv)),
247 &field.schema,
248 )?;
249 }
250 writer.dec_ident();
251
252 writeln!(writer, "def __repr__(self):")?;
254 writer.inc_ident();
255 writeln!(writer, "return \"{}(\" + \\", struc.name.camel_case(conv))?;
256 for (index, field) in struc.fields.iter().enumerate() {
257 write!(writer, " repr(self.{})", field.name.snake_case(conv))?;
258 if index + 1 < struc.fields.len() {
259 write!(writer, " + \",\"")?;
260 }
261 writeln!(writer, " + \\")?;
262 }
263 writeln!(writer, " \")\"")?;
264 writer.dec_ident();
265
266 writer.dec_ident();
267
268 if let Some((base_name, _)) = base {
269 writeln!(
270 writer,
271 "{}.{} = {}",
272 base_name.camel_case(conv),
273 struc.name.camel_case(conv),
274 struc.name.camel_case(conv)
275 )?;
276 }
277
278 Ok(())
279}
280
281impl trans_gen_core::Generator for Generator {
282 fn new(name: &str, version: &str) -> Self {
283 let mut files = HashMap::new();
284 files.insert(
285 "stream_wrapper.py".to_owned(),
286 include_str!("../template/stream_wrapper.py").to_owned(),
287 );
288 Self {
289 model_init: String::new(),
290 files,
291 }
292 }
293 fn result(mut self) -> HashMap<String, String> {
294 if !self.model_init.is_empty() {
295 self.files
296 .insert("model/__init__.py".to_owned(), self.model_init);
297 }
298 self.files
299 }
300 fn add_only(&mut self, schema: &Schema) {
301 match schema {
302 Schema::Enum {
303 base_name,
304 variants,
305 } => {
306 let file_name = format!("model/{}.py", base_name.snake_case(conv));
307 let mut writer = Writer::new();
308 writeln!(writer, "from enum import IntEnum").unwrap();
309 writeln!(writer).unwrap();
310 writeln!(writer, "class {}(IntEnum):", base_name.camel_case(conv)).unwrap();
311 writer.inc_ident();
312 for (index, variant) in variants.iter().enumerate() {
313 writeln!(writer, "{} = {}", variant.shouty_snake_case(conv), index).unwrap();
314 }
315 writer.dec_ident();
316 writeln!(
317 &mut self.model_init,
318 "from .{} import {}",
319 base_name.snake_case(conv),
320 base_name.camel_case(conv),
321 )
322 .unwrap();
323 self.files.insert(file_name, writer.get());
324 }
325 Schema::Struct(struc) => {
326 let file_name = format!("model/{}.py", struc.name.snake_case(conv));
327 let mut writer = Writer::new();
328 write_struct(&mut writer, struc, None).unwrap();
329 writeln!(
330 &mut self.model_init,
331 "from .{} import {}",
332 struc.name.snake_case(conv),
333 struc.name.camel_case(conv)
334 )
335 .unwrap();
336 self.files.insert(file_name, writer.get());
337 }
338 Schema::OneOf {
339 base_name,
340 variants,
341 } => {
342 let file_name = format!("model/{}.py", base_name.snake_case(conv));
343 let mut writer = Writer::new();
344 writeln!(&mut writer, "class {}:", base_name.camel_case(conv)).unwrap();
345 {
346 writer.inc_ident();
347 writeln!(&mut writer, "@staticmethod").unwrap();
348 writeln!(&mut writer, "def read_from(stream):").unwrap();
349 {
350 writer.inc_ident();
351 writeln!(&mut writer, "discriminant = stream.read_int()").unwrap();
352 for variant in variants {
353 writeln!(
354 &mut writer,
355 "if discriminant == {}.TAG:",
356 variant.name.camel_case(conv)
357 )
358 .unwrap();
359 writeln!(
360 &mut writer,
361 " return {}.{}.read_from(stream)",
362 base_name.camel_case(conv),
363 variant.name.camel_case(conv),
364 )
365 .unwrap();
366 }
367 writeln!(
368 &mut writer,
369 "raise Exception(\"Unexpected discriminant value\")"
370 )
371 .unwrap();
372 writer.dec_ident();
373 }
374 writer.dec_ident();
375 }
376 writeln!(&mut writer).unwrap();
377 for (discriminant, variant) in variants.iter().enumerate() {
378 write_struct(&mut writer, variant, Some((base_name, discriminant))).unwrap();
379 }
380 writeln!(
381 &mut self.model_init,
382 "from .{} import {}",
383 base_name.snake_case(conv),
384 base_name.camel_case(conv)
385 )
386 .unwrap();
387 self.files.insert(file_name, writer.get());
388 }
389 Schema::Bool
390 | Schema::Int32
391 | Schema::Int64
392 | Schema::Float32
393 | Schema::Float64
394 | Schema::String
395 | Schema::Option(_)
396 | Schema::Vec(_)
397 | Schema::Map(_, _) => {}
398 }
399 }
400}