trans_gen/gens/python/
mod.rs

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