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 .replace("Params", "Parameters")
12}
13
14pub struct Generator {
15 main_namespace: String,
16 files: HashMap<String, String>,
17}
18
19fn is_null(value: &str, schema: &Schema) -> String {
20 if nullable(schema) {
21 format!("{} == null", value)
22 } else {
23 format!("!{}.HasValue", value)
24 }
25}
26
27fn option_unwrap(name: &str, schema: &Schema) -> String {
28 if nullable(schema) {
29 name.to_owned()
30 } else {
31 format!("{}.Value", name)
32 }
33}
34
35fn nullable(schema: &Schema) -> bool {
36 match schema {
37 Schema::Bool
38 | Schema::Int32
39 | Schema::Int64
40 | Schema::Float32
41 | Schema::Float64
42 | Schema::Enum { .. }
43 | Schema::Struct(_) => false,
44 Schema::String
45 | Schema::Option(_)
46 | Schema::Vec(_)
47 | Schema::Map(_, _)
48 | Schema::OneOf { .. } => true,
49 }
50}
51
52fn type_name(schema: &Schema) -> String {
53 format!("{}{}", type_name_prearray(schema), type_post_array(schema))
54}
55
56fn type_name_prearray(schema: &Schema) -> String {
57 match schema {
58 Schema::Bool => "bool".to_owned(),
59 Schema::Int32 => "int".to_owned(),
60 Schema::Int64 => "long".to_owned(),
61 Schema::Float32 => "float".to_owned(),
62 Schema::Float64 => "double".to_owned(),
63 Schema::String => "string".to_owned(),
64 Schema::Struct(Struct { name, .. })
65 | Schema::OneOf {
66 base_name: name, ..
67 }
68 | Schema::Enum {
69 base_name: name, ..
70 } => format!("Model.{}", name.camel_case(conv)),
71 Schema::Option(inner) => {
72 if nullable(inner) {
73 type_name(inner)
74 } else {
75 format!("{}?", type_name(inner))
76 }
77 }
78 Schema::Vec(inner) => type_name_prearray(inner),
79 Schema::Map(key, value) => format!(
80 "System.Collections.Generic.IDictionary<{}, {}>",
81 type_name(key),
82 type_name(value)
83 ),
84 }
85}
86
87fn type_post_array(schema: &Schema) -> String {
88 match schema {
89 Schema::Vec(inner) => format!("[]{}", type_post_array(inner)),
90 _ => String::new(),
91 }
92}
93
94fn index_var_name(index_var: &mut usize) -> String {
95 let result = "ijk".chars().nth(*index_var).unwrap();
96 *index_var += 1;
97 result.to_string()
98}
99
100fn var_name(name: &str) -> &str {
101 match name.rfind('.') {
102 Some(index) => &name[(index + 1)..],
103 None => name,
104 }
105}
106
107fn write_struct(
108 writer: &mut Writer,
109 struc: &Struct,
110 base: Option<(&Name, usize)>,
111) -> std::fmt::Result {
112 if let Some((base_name, _)) = base {
114 writeln!(
115 writer,
116 "public class {} : {}",
117 struc.name.camel_case(conv),
118 base_name.camel_case(conv)
119 )?;
120 } else {
121 writeln!(writer, "public struct {}", struc.name.camel_case(conv))?;
122 }
123 writeln!(writer, "{{")?;
124 writer.inc_ident();
125 if let Some((_, discriminant)) = base {
126 writeln!(writer, "public const int TAG = {};", discriminant)?;
127 }
128
129 for field in &struc.fields {
131 writeln!(
132 writer,
133 "public {} {} {{ get; set; }}",
134 type_name(&field.schema),
135 field.name.camel_case(conv)
136 )?;
137 }
138
139 if base.is_some() {
141 writeln!(writer, "public {}() {{}}", struc.name.camel_case(conv))?;
142 }
143 if !struc.fields.is_empty() {
144 write!(writer, "public {}(", struc.name.camel_case(conv))?;
145 for (index, field) in struc.fields.iter().enumerate() {
146 if index > 0 {
147 write!(writer, ", ")?;
148 }
149 write!(
150 writer,
151 "{} {}",
152 type_name(&field.schema),
153 field.name.mixed_case(conv)
154 )?;
155 }
156 writeln!(writer, ")")?;
157 writeln!(writer, "{{")?;
158 for field in &struc.fields {
159 writeln!(
160 writer,
161 " this.{} = {};",
162 field.name.camel_case(conv),
163 field.name.mixed_case(conv)
164 )?;
165 }
166 writeln!(writer, "}}")?;
167 }
168
169 writeln!(
171 writer,
172 "public static {}{} ReadFrom(System.IO.BinaryReader reader)",
173 if base.is_some() { "new " } else { "" },
174 struc.name.camel_case(conv)
175 )?;
176 writeln!(writer, "{{")?;
177 writer.inc_ident();
178 writeln!(
179 writer,
180 "var result = new {}();",
181 struc.name.camel_case(conv)
182 )?;
183 for field in &struc.fields {
184 fn assign(
185 writer: &mut Writer,
186 to: &str,
187 schema: &Schema,
188 index_var: &mut usize,
189 ) -> std::fmt::Result {
190 match schema {
191 Schema::Bool => {
192 writeln!(writer, "{} = reader.ReadBoolean();", to)?;
193 }
194 Schema::Int32 => {
195 writeln!(writer, "{} = reader.ReadInt32();", to)?;
196 }
197 Schema::Int64 => {
198 writeln!(writer, "{} = reader.ReadInt64();", to)?;
199 }
200 Schema::Float32 => {
201 writeln!(writer, "{} = reader.ReadSingle();", to)?;
202 }
203 Schema::Float64 => {
204 writeln!(writer, "{} = reader.ReadDouble();", to)?;
205 }
206 Schema::String => {
207 writeln!(writer, "{} = System.Text.Encoding.UTF8.GetString(reader.ReadBytes(reader.ReadInt32()));", to)?;
208 }
209 Schema::Struct(Struct { name, .. })
210 | Schema::OneOf {
211 base_name: name, ..
212 } => {
213 writeln!(
214 writer,
215 "{} = Model.{}.ReadFrom(reader);",
216 to,
217 name.camel_case(conv)
218 )?;
219 }
220 Schema::Option(inner) => {
221 writeln!(writer, "if (reader.ReadBoolean())")?;
222 writeln!(writer, "{{")?;
223 writer.inc_ident();
224 assign(writer, to, inner, index_var)?;
225 writer.dec_ident();
226 writeln!(writer, "}} else")?;
227 writeln!(writer, "{{")?;
228 writeln!(writer, " {} = null;", to)?;
229 writeln!(writer, "}}")?;
230 }
231 Schema::Vec(inner) => {
232 writeln!(
233 writer,
234 "{} = new {}[reader.ReadInt32()]{};",
235 to,
236 type_name_prearray(inner),
237 type_post_array(inner)
238 )?;
239 let index_var_name = index_var_name(index_var);
240 writeln!(
241 writer,
242 "for (int {} = 0; {} < {}.Length; {}++)",
243 index_var_name, index_var_name, to, index_var_name
244 )?;
245 writeln!(writer, "{{")?;
246 writer.inc_ident();
247 assign(
248 writer,
249 &format!("{}[{}]", to, index_var_name),
250 inner,
251 index_var,
252 )?;
253 writer.dec_ident();
254 writeln!(writer, "}}")?;
255 }
256 Schema::Map(key_type, value_type) => {
257 let to_size = format!("{}Size", var_name(to));
258 writeln!(writer, "int {} = reader.ReadInt32();", to_size)?;
259 writeln!(
260 writer,
261 "{} = new System.Collections.Generic.Dictionary<{}, {}>({});",
262 to,
263 type_name(key_type),
264 type_name(value_type),
265 to_size
266 )?;
267 let index_var_name = index_var_name(index_var);
268 writeln!(
269 writer,
270 "for (int {} = 0; {} < {}; {}++)",
271 index_var_name, index_var_name, to_size, index_var_name
272 )?;
273 writeln!(writer, "{{")?;
274 writer.inc_ident();
275 writeln!(writer, "{} {}Key;", type_name(key_type), var_name(to))?;
276 assign(writer, &format!("{}Key", var_name(to)), key_type, index_var)?;
277 writeln!(writer, "{} {}Value;", type_name(value_type), var_name(to))?;
278 assign(
279 writer,
280 &format!("{}Value", var_name(to)),
281 value_type,
282 index_var,
283 )?;
284 writeln!(
285 writer,
286 "{}.Add({}Key, {}Value);",
287 to,
288 var_name(to),
289 var_name(to)
290 )?;
291 writer.dec_ident();
292 writeln!(writer, "}}")?;
293 }
294 Schema::Enum {
295 base_name,
296 variants,
297 } => {
298 writeln!(writer, "switch (reader.ReadInt32())")?;
299 writeln!(writer, "{{")?;
300 for (discriminant, variant) in variants.iter().enumerate() {
301 writeln!(writer, "case {}:", discriminant)?;
302 writeln!(
303 writer,
304 " {} = Model.{}.{};",
305 to,
306 base_name.camel_case(conv),
307 variant.camel_case(conv)
308 )?;
309 writeln!(writer, " break;")?;
310 }
311 writeln!(writer, "default:")?;
312 writeln!(
313 writer,
314 " throw new System.Exception(\"Unexpected discriminant value\");"
315 )?;
316 writeln!(writer, "}}")?;
317 }
318 }
319 Ok(())
320 }
321 assign(
322 writer,
323 &format!("result.{}", field.name.camel_case(conv)),
324 &field.schema,
325 &mut 0,
326 )?;
327 }
328 writeln!(writer, "return result;")?;
329 writer.dec_ident();
330 writeln!(writer, "}}")?;
331
332 writeln!(
334 writer,
335 "public {}void WriteTo(System.IO.BinaryWriter writer)",
336 if base.is_some() { "override " } else { "" }
337 )?;
338 writeln!(writer, "{{")?;
339 writer.inc_ident();
340 if base.is_some() {
341 writeln!(writer, "writer.Write(TAG);")?;
342 }
343 if let Some(magic) = struc.magic {
344 writeln!(writer, "writer.Write({});", magic)?;
345 }
346 for field in &struc.fields {
347 fn write(writer: &mut Writer, value: &str, schema: &Schema) -> std::fmt::Result {
348 match schema {
349 Schema::Bool => {
350 writeln!(writer, "writer.Write({});", value)?;
351 }
352 Schema::Int32 => {
353 writeln!(writer, "writer.Write({});", value)?;
354 }
355 Schema::Int64 => {
356 writeln!(writer, "writer.Write({});", value)?;
357 }
358 Schema::Float32 => {
359 writeln!(writer, "writer.Write({});", value)?;
360 }
361 Schema::Float64 => {
362 writeln!(writer, "writer.Write({});", value)?;
363 }
364 Schema::String => {
365 let data_var = format!("{}Data", var_name(value));
366 writeln!(
367 writer,
368 "var {} = System.Text.Encoding.UTF8.GetBytes({});",
369 data_var, value
370 )?;
371 writeln!(writer, "writer.Write({}.Length);", data_var)?;
372 writeln!(writer, "writer.Write({});", data_var)?;
373 }
374 Schema::Struct(_) | Schema::OneOf { .. } => {
375 writeln!(writer, "{}.WriteTo(writer);", value)?;
376 }
377 Schema::Option(inner) => {
378 writeln!(writer, "if ({})", is_null(value, inner))?;
379 writeln!(writer, "{{")?;
380 writeln!(writer, " writer.Write(false);")?;
381 writeln!(writer, "}} else")?;
382 writeln!(writer, "{{")?;
383 writer.inc_ident();
384 writeln!(writer, "writer.Write(true);")?;
385 write(writer, &option_unwrap(value, inner), inner)?;
386 writer.dec_ident();
387 writeln!(writer, "}}")?;
388 }
389 Schema::Vec(inner) => {
390 writeln!(writer, "writer.Write({}.Length);", value)?;
391 writeln!(
392 writer,
393 "foreach (var {}Element in {})",
394 var_name(value),
395 value
396 )?;
397 writeln!(writer, "{{")?;
398 writer.inc_ident();
399 write(writer, &format!("{}Element", var_name(value)), inner)?;
400 writer.dec_ident();
401 writeln!(writer, "}}")?;
402 }
403 Schema::Map(key_type, value_type) => {
404 writeln!(writer, "writer.Write({}.Count);", value)?;
405 writeln!(
406 writer,
407 "foreach (var {}Entry in {})",
408 var_name(value),
409 value
410 )?;
411 writeln!(writer, "{{")?;
412 writer.inc_ident();
413 writeln!(
414 writer,
415 "var {}Key = {}Entry.Key;",
416 var_name(value),
417 var_name(value)
418 )?;
419 writeln!(
420 writer,
421 "var {}Value = {}Entry.Value;",
422 var_name(value),
423 var_name(value)
424 )?;
425 write(writer, &format!("{}Key", var_name(value)), key_type)?;
426 write(writer, &format!("{}Value", var_name(value)), value_type)?;
427 writer.dec_ident();
428 writeln!(writer, "}}")?;
429 }
430 Schema::Enum { .. } => {
431 writeln!(writer, "writer.Write((int) ({}));", value)?;
432 }
433 }
434 Ok(())
435 }
436 write(writer, &field.name.camel_case(conv), &field.schema)?;
437 }
438 writer.dec_ident();
439 writeln!(writer, "}}")?;
440 writer.dec_ident();
441 writeln!(writer, "}}")?;
442 Ok(())
443}
444
445impl trans_gen_core::Generator for Generator {
446 fn new(name: &str, version: &str) -> Self {
447 Self {
448 main_namespace: Name::new(name.to_owned()).camel_case(conv),
449 files: HashMap::new(),
450 }
451 }
452 fn result(self) -> HashMap<String, String> {
453 self.files
454 }
455 fn add_only(&mut self, schema: &Schema) {
456 match schema {
457 Schema::Enum {
458 base_name,
459 variants,
460 } => {
461 let file_name = format!("Model/{}.cs", base_name.camel_case(conv));
462 let mut writer = Writer::new();
463 writeln!(writer, "namespace {}.Model", self.main_namespace).unwrap();
464 writeln!(writer, "{{").unwrap();
465 writer.inc_ident();
466 writeln!(writer, "public enum {}", base_name.camel_case(conv)).unwrap();
467 writeln!(writer, "{{").unwrap();
468 writer.inc_ident();
469 for (discriminant, variant) in variants.iter().enumerate() {
470 writeln!(writer, "{} = {},", variant.camel_case(conv), discriminant,).unwrap();
471 }
472 writer.dec_ident();
473 writeln!(writer, "}}").unwrap();
474 writer.dec_ident();
475 writeln!(writer, "}}").unwrap();
476 self.files.insert(file_name, writer.get());
477 }
478 Schema::Struct(struc) => {
479 let file_name = format!("Model/{}.cs", struc.name.camel_case(conv));
480 let mut writer = Writer::new();
481 writeln!(writer, "namespace {}.Model", self.main_namespace).unwrap();
482 writeln!(writer, "{{").unwrap();
483 writer.inc_ident();
484 write_struct(&mut writer, struc, None).unwrap();
485 writer.dec_ident();
486 writeln!(writer, "}}").unwrap();
487 self.files.insert(file_name, writer.get());
488 }
489 Schema::OneOf {
490 base_name,
491 variants,
492 } => {
493 let file_name = format!("Model/{}.cs", base_name.camel_case(conv));
494 let mut writer = Writer::new();
495 writeln!(writer, "namespace {}.Model", self.main_namespace).unwrap();
496 writeln!(writer, "{{").unwrap();
497 writer.inc_ident();
498 writeln!(
499 &mut writer,
500 "public abstract class {}",
501 base_name.camel_case(conv)
502 )
503 .unwrap();
504 writeln!(writer, "{{").unwrap();
505 {
506 writer.inc_ident();
507 writeln!(
508 &mut writer,
509 "public abstract void WriteTo(System.IO.BinaryWriter writer);"
510 )
511 .unwrap();
512 writeln!(
513 &mut writer,
514 "public static {} ReadFrom(System.IO.BinaryReader reader)",
515 base_name.camel_case(conv)
516 )
517 .unwrap();
518 writeln!(writer, "{{").unwrap();
519 {
520 writer.inc_ident();
521 writeln!(&mut writer, "switch (reader.ReadInt32())").unwrap();
522 writeln!(writer, "{{").unwrap();
523 writer.inc_ident();
524 for variant in variants {
525 writeln!(&mut writer, "case {}.TAG:", variant.name.camel_case(conv))
526 .unwrap();
527 writeln!(
528 &mut writer,
529 " return {}.ReadFrom(reader);",
530 variant.name.camel_case(conv)
531 )
532 .unwrap();
533 }
534 writeln!(&mut writer, "default:").unwrap();
535 writeln!(
536 &mut writer,
537 " throw new System.Exception(\"Unexpected discriminant value\");"
538 )
539 .unwrap();
540 writer.dec_ident();
541 writeln!(&mut writer, "}}").unwrap();
542 writer.dec_ident();
543 }
544 writeln!(&mut writer, "}}").unwrap();
545 for (discriminant, variant) in variants.iter().enumerate() {
546 writeln!(&mut writer).unwrap();
547 write_struct(&mut writer, variant, Some((base_name, discriminant)))
548 .unwrap();
549 }
550 writer.dec_ident();
551 }
552 writeln!(&mut writer, "}}").unwrap();
553 writer.dec_ident();
554 writeln!(writer, "}}").unwrap();
555 self.files.insert(file_name, writer.get());
556 }
557 Schema::Bool
558 | Schema::Int32
559 | Schema::Int64
560 | Schema::Float32
561 | Schema::Float64
562 | Schema::String
563 | Schema::Option(_)
564 | Schema::Vec(_)
565 | Schema::Map(_, _) => {}
566 }
567 }
568}