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