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 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 for field in &struc.fields {
83 writeln!(writer, "attr_accessor :{}", field.name.snake_case(conv))?;
84 }
85
86 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 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 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}