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