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