trans_gen_ruby/
lib.rs

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