1use crate::{
5 common,
6 indent::{IndentConfig, IndentedWriter},
7 CodeGeneratorConfig, Encoding,
8};
9use heck::{CamelCase, MixedCase, SnakeCase};
10use include_dir::include_dir as include_directory;
11use serde_reflection::{ContainerFormat, Format, FormatHolder, Named, Registry, VariantFormat};
12use std::{
13 collections::{BTreeMap, HashMap},
14 io::{Result, Write},
15 path::{Path, PathBuf},
16};
17
18pub struct CodeGenerator<'a> {
20 config: &'a CodeGeneratorConfig,
22}
23
24struct DartEmitter<'a, T> {
26 out: IndentedWriter<T>,
28 generator: &'a CodeGenerator<'a>,
30 current_namespace: Vec<String>,
32 registry: &'a Registry,
34}
35
36impl<'a> CodeGenerator<'a> {
37 pub fn new(config: &'a CodeGeneratorConfig) -> Self {
39 let mut external_qualified_names = HashMap::new();
40 for (namespace, names) in &config.external_definitions {
41 for name in names {
42 external_qualified_names.insert(name.to_string(), format!("{namespace}.{name}"));
43 }
44 }
45 Self { config }
46 }
47
48 pub fn output(&self, install_dir: std::path::PathBuf, registry: &Registry) -> Result<()> {
50 let current_namespace = self
51 .config
52 .module_name
53 .split('.')
54 .map(String::from)
55 .collect::<Vec<_>>();
56
57 let mut dir_path = install_dir;
58 std::fs::create_dir_all(&dir_path)?;
59 dir_path = dir_path.join("lib").join("src");
60 for part in ¤t_namespace {
61 dir_path = dir_path.join(part);
62 }
63 std::fs::create_dir_all(&dir_path)?;
64
65 for (name, format) in registry {
66 self.write_container_class(
67 &dir_path,
68 current_namespace.clone(),
69 name,
70 format,
71 registry,
72 )?;
73 }
74 self.write_helper_class(&dir_path, current_namespace.clone(), registry)?;
75 self.write_library(&dir_path, current_namespace, registry)?;
76 Ok(())
77 }
78
79 fn write_library(
80 &self,
81 install_dir: &Path,
82 current_namespace: Vec<String>,
83 registry: &Registry,
84 ) -> Result<()> {
85 let mut file =
86 std::fs::File::create(install_dir.join(self.config.module_name.clone() + ".dart"))?;
87 let mut emitter = DartEmitter {
88 out: IndentedWriter::new(&mut file, IndentConfig::Space(2)),
89 generator: self,
90 current_namespace,
91 registry,
92 };
93
94 writeln!(
95 &mut emitter.out,
96 r#"// ignore_for_file: unused_import
97library {}_types;
98
99import 'dart:typed_data';
100import 'package:meta/meta.dart';
101import 'package:tuple/tuple.dart';
102import '../serde/serde.dart';"#,
103 self.config.module_name,
104 )?;
105
106 for encoding in &self.config.encodings {
107 writeln!(
108 &mut emitter.out,
109 "import '../{0}/{0}.dart';",
110 encoding.name()
111 )?;
112 }
113
114 if let Some(files) = &self.config.external_definitions.get("import") {
115 for file in *files {
116 writeln!(&mut emitter.out, "import '{file}';")?;
117 }
118 }
119
120 writeln!(&mut emitter.out, "\nexport '../serde/serde.dart';")?;
121
122 writeln!(&mut emitter.out, "\npart 'trait_helpers.dart';")?;
123 for name in registry.keys() {
124 writeln!(&mut emitter.out, "part '{}.dart';", name.to_snake_case())?;
125 }
126
127 Ok(())
128 }
129
130 fn write_container_class(
131 &self,
132 dir_path: &std::path::Path,
133 current_namespace: Vec<String>,
134 name: &str,
135 format: &ContainerFormat,
136 registry: &Registry,
137 ) -> Result<()> {
138 let mut file =
139 std::fs::File::create(dir_path.join(name.to_string().to_snake_case() + ".dart"))?;
140 let mut emitter = DartEmitter {
141 out: IndentedWriter::new(&mut file, IndentConfig::Space(2)),
142 generator: self,
143 current_namespace,
144 registry,
145 };
146
147 emitter.output_preamble()?;
148 emitter.output_container(name, format)
149 }
150
151 fn write_helper_class(
152 &self,
153 dir_path: &std::path::Path,
154 current_namespace: Vec<String>,
155 registry: &Registry,
156 ) -> Result<()> {
157 let mut file = std::fs::File::create(dir_path.join("trait_helpers.dart"))?;
158 let mut emitter = DartEmitter {
159 out: IndentedWriter::new(&mut file, IndentConfig::Space(2)),
160 generator: self,
161 current_namespace,
162 registry,
163 };
164
165 emitter.output_preamble()?;
166 emitter.output_trait_helpers(registry)
167 }
168}
169
170impl<'a, T> DartEmitter<'a, T>
171where
172 T: Write,
173{
174 fn output_preamble(&mut self) -> Result<()> {
175 writeln!(
176 self.out,
177 "// ignore_for_file: type=lint, type=warning\npart of '{}.dart';",
178 self.generator.config.module_name
179 )?;
180
181 Ok(())
182 }
183
184 fn get_field_container_type(&self, name: &str) -> Option<&ContainerFormat> {
185 match self.registry.get(name) {
186 Some(container) => Some(container),
187 None => None,
188 }
189 }
190
191 fn get_class(&self, name: &str) -> String {
194 if self.generator.config.enums.c_style {
195 use ContainerFormat::Enum;
196 match self.get_field_container_type(name) {
197 Some(Enum(variants))
200 if variants.values().all(|f| f.value == VariantFormat::Unit) =>
201 {
202 format!("{name}Extension")
203 }
204 _ => name.to_string(),
205 }
206 } else {
207 name.to_string()
208 }
209 }
210
211 fn quote_qualified_name(&self, name: &str) -> String {
212 match name {
213 "List" => "List_".to_string(),
214 "Map" => "Map_".to_string(),
215 name => name.to_string(),
216 }
217 }
218
219 fn quote_field(&self, name: &str) -> String {
220 match name {
221 "hashCode" => "hashCode_".to_string(),
222 "runtimeType" => "runtimeType_".to_string(),
223 name => name.to_string(),
224 }
225 }
226
227 fn quote_type(&self, format: &Format) -> String {
228 use Format::*;
229 match format {
230 TypeName(x) => self.quote_qualified_name(x),
231 Unit => "Unit".into(),
232 Bool => "bool".into(),
233 I8 => "int".into(),
234 I16 => "int".into(),
235 I32 => "int".into(),
236 I64 => "int".into(),
237 I128 => "Int128".into(),
238 U8 => "int".into(),
239 U16 => "int".into(),
240 U32 => "int".into(),
241 U64 => "Uint64".into(),
242 U128 => "Uint128".into(),
243 F32 => "double".into(),
244 F64 => "double".into(),
245 Char => "int".into(),
246 Str => "String".into(),
247 Bytes => "Bytes".into(),
248
249 Option(format) => format!("{}?", self.quote_type(format)),
250 Seq(format) => format!("List<{}>", self.quote_type(format)),
251 Map { key, value } => {
252 format!("Map<{}, {}>", self.quote_type(key), self.quote_type(value))
253 }
254 Tuple(formats) => format!("Tuple{}<{}>", formats.len(), self.quote_types(formats)),
255 TupleArray { content, size: _ } => format!("List<{}>", self.quote_type(content)),
256 Variable(_) => panic!("unexpected value"),
257 }
258 }
259
260 fn quote_types(&self, formats: &[Format]) -> String {
261 formats
262 .iter()
263 .map(|f| self.quote_type(f))
264 .collect::<Vec<_>>()
265 .join(", ")
266 }
267
268 fn quote_serialize_value(&self, value: &str, format: &Format) -> String {
269 use Format::*;
270 match format {
271 TypeName(_) => format!("{value}.serialize(serializer);"),
272 Unit => format!("serializer.serializeUnit({value});"),
273 Bool => format!("serializer.serializeBool({value});"),
274 I8 => format!("serializer.serializeInt8({value});"),
275 I16 => format!("serializer.serializeInt16({value});"),
276 I32 => format!("serializer.serializeInt32({value});"),
277 I64 => format!("serializer.serializeInt64({value});"),
278 I128 => format!("serializer.serializeInt128({value});"),
279 U8 => format!("serializer.serializeUint8({value});"),
280 U16 => format!("serializer.serializeUint16({value});"),
281 U32 => format!("serializer.serializeUint32({value});"),
282 U64 => format!("serializer.serializeUint64({value});"),
283 U128 => format!("serializer.serializeUint128({value});"),
284 F32 => format!("serializer.serializeFloat32({value});"),
285 F64 => format!("serializer.serializeFloat64({value});"),
286 Char => format!("serializer.serializeChar({value});"),
287 Str => format!("serializer.serializeString({value});"),
288 Bytes => format!("serializer.serializeBytes({value});"),
289 _ => format!(
290 "{}.serialize{}({}, serializer);",
291 self.quote_qualified_name("TraitHelpers"),
292 common::mangle_type(format).to_camel_case(),
293 value
294 ),
295 }
296 }
297
298 fn quote_deserialize(&self, format: &Format) -> String {
299 use Format::*;
300 match format {
301 TypeName(name) => {
302 format!(
303 "{}.deserialize(deserializer)",
304 self.quote_qualified_name(&self.get_class(name))
305 )
306 }
307 Unit => "deserializer.deserializeUnit()".to_string(),
308 Bool => "deserializer.deserializeBool()".to_string(),
309 I8 => "deserializer.deserializeInt8()".to_string(),
310 I16 => "deserializer.deserializeInt16()".to_string(),
311 I32 => "deserializer.deserializeInt32()".to_string(),
312 I64 => "deserializer.deserializeInt64()".to_string(),
313 I128 => "deserializer.deserializeInt128()".to_string(),
314 U8 => "deserializer.deserializeUint8()".to_string(),
315 U16 => "deserializer.deserializeUint16()".to_string(),
316 U32 => "deserializer.deserializeUint32()".to_string(),
317 U64 => "deserializer.deserializeUint64()".to_string(),
318 U128 => "deserializer.deserializeUint128()".to_string(),
319 F32 => "deserializer.deserializeFloat32()".to_string(),
320 F64 => "deserializer.deserializeFloat64()".to_string(),
321 Char => "deserializer.deserializeChar()".to_string(),
322 Str => "deserializer.deserializeString()".to_string(),
323 Bytes => "deserializer.deserializeBytes()".to_string(),
324 _ => format!(
325 "{}.deserialize{}(deserializer)",
326 self.quote_qualified_name("TraitHelpers"),
327 common::mangle_type(format).to_camel_case(),
328 ),
329 }
330 }
331
332 fn enter_class(&mut self, name: &str) {
333 self.out.indent();
334 self.current_namespace.push(name.to_string());
335 }
336
337 fn leave_class(&mut self) {
338 self.out.unindent();
339 self.current_namespace.pop();
340 }
341
342 fn output_trait_helpers(&mut self, registry: &Registry) -> Result<()> {
343 let mut subtypes = BTreeMap::new();
344 for format in registry.values() {
345 format
346 .visit(&mut |f| {
347 if Self::needs_helper(f) {
348 subtypes.insert(common::mangle_type(f), f.clone());
349 }
350 Ok(())
351 })
352 .unwrap();
353 }
354 writeln!(self.out, "class TraitHelpers {{")?;
355 self.enter_class("TraitHelpers");
356 for (mangled_name, subtype) in &subtypes {
357 self.output_serialization_helper(mangled_name, subtype)?;
358 self.output_deserialization_helper(mangled_name, subtype)?;
359 }
360 self.leave_class();
361 writeln!(self.out, "}}\n")
362 }
363
364 fn needs_helper(format: &Format) -> bool {
365 use Format::*;
366 matches!(
367 format,
368 Option(_) | Seq(_) | Map { .. } | Tuple(_) | TupleArray { .. }
369 )
370 }
371
372 fn output_serialization_helper(&mut self, name: &str, format0: &Format) -> Result<()> {
373 use Format::*;
374
375 write!(
376 self.out,
377 "static void serialize{}({} value, BinarySerializer serializer) {{",
378 name.to_camel_case(),
379 self.quote_type(format0)
380 )?;
381 self.out.indent();
382 match format0 {
383 Option(format) => {
384 write!(
385 self.out,
386 r#"
387if (value == null) {{
388 serializer.serializeOptionTag(false);
389}} else {{
390 serializer.serializeOptionTag(true);
391 {}
392}}
393"#,
394 self.quote_serialize_value("value", format)
395 )?;
396 }
397
398 Seq(format) => {
399 write!(
400 self.out,
401 r#"
402serializer.serializeLength(value.length);
403for (final item in value) {{
404 {}
405}}
406"#,
407 self.quote_serialize_value("item", format)
408 )?;
409 }
410
411 Map { key, value } => {
412 write!(
413 self.out,
414 r#"
415serializer.serializeLength(value.length);
416final offsets = List<int>.filled(value.length, 0);
417var count = 0;
418value.entries.forEach((entry) {{
419 offsets[count++] = serializer.offset;
420 {}
421 {}
422}});
423"#,
424 self.quote_serialize_value("entry.key", key),
425 self.quote_serialize_value("entry.value", value)
426 )?;
427 }
428
429 Tuple(formats) => {
430 writeln!(self.out)?;
431 for (index, format) in formats.iter().enumerate() {
432 let expr = format!("value.item{}", index + 1);
433 writeln!(self.out, "{}", self.quote_serialize_value(&expr, format))?;
434 }
435 }
436
437 TupleArray { content, size } => {
438 write!(
439 self.out,
440 r#"
441assert (value.length == {});
442for (final item in value) {{
443 {}
444}}
445"#,
446 size,
447 self.quote_serialize_value("item", content),
448 )?;
449 }
450
451 _ => panic!("unexpected case"),
452 }
453 self.out.unindent();
454 writeln!(self.out, "}}\n")
455 }
456
457 fn output_deserialization_helper(&mut self, name: &str, format0: &Format) -> Result<()> {
458 use Format::*;
459
460 write!(
461 self.out,
462 "static {} deserialize{}(BinaryDeserializer deserializer) {{",
463 self.quote_type(format0),
464 name.to_camel_case(),
465 )?;
466 self.out.indent();
467 match format0 {
468 Option(format) => {
469 write!(
470 self.out,
471 r#"
472final tag = deserializer.deserializeOptionTag();
473if (tag) {{
474 return {};
475}} else {{
476 return null;
477}}
478"#,
479 self.quote_deserialize(format),
480 )?;
481 }
482
483 Seq(format) => {
484 write!(
485 self.out,
486 r#"
487final length = deserializer.deserializeLength();
488return List.generate(length, (_) => {0});
489"#,
490 self.quote_deserialize(format)
491 )?;
492 }
493
494 Map { key, value } => {
495 write!(
496 self.out,
497 r#"
498final length = deserializer.deserializeLength();
499final obj = <{0}, {1}>{{}};
500var previousKeyStart = 0;
501var previousKeyEnd = 0;
502for (var i = 0; i < length; i++) {{
503 final keyStart = deserializer.offset;
504 {0} key = {2};
505 final keyEnd = deserializer.offset;
506 if (i > 0) {{
507 deserializer.checkThatKeySlicesAreIncreasing(
508 Slice(previousKeyStart, previousKeyEnd),
509 Slice(keyStart, keyEnd),
510 );
511 }}
512 previousKeyStart = keyStart;
513 previousKeyEnd = keyEnd;
514 {1} value = {3};
515 obj.putIfAbsent(key, () => value);
516}}
517return obj;
518"#,
519 self.quote_type(key),
520 self.quote_type(value),
521 self.quote_deserialize(key),
522 self.quote_deserialize(value),
523 )?;
524 }
525
526 Tuple(formats) => {
527 write!(
528 self.out,
529 r#"
530return {}({}
531);
532"#,
533 self.quote_type(format0),
534 formats
535 .iter()
536 .map(|f| format!("\n {}", self.quote_deserialize(f)))
537 .collect::<Vec<_>>()
538 .join(",")
539 )?;
540 }
541
542 TupleArray { content, size } => {
543 write!(
544 self.out,
545 r#"
546final obj = List<{0}>.filled({1}, 0);
547for (var i = 0; i < {1}; i++) {{
548 obj[i] = {2};
549}}
550return obj;
551"#,
552 self.quote_type(content),
553 size,
554 self.quote_deserialize(content)
555 )?;
556 }
557
558 _ => panic!("unexpected case"),
559 }
560 self.out.unindent();
561 writeln!(self.out, "}}\n")
562 }
563
564 fn output_container(&mut self, name: &str, format: &ContainerFormat) -> Result<()> {
565 use ContainerFormat::*;
566 let fields = match format {
567 UnitStruct => Vec::new(),
568 NewTypeStruct(format) => {
569 vec![Named {
570 name: "value".to_string(),
571 value: format.as_ref().clone(),
572 }]
573 }
574 TupleStruct(formats) => formats
575 .iter()
576 .enumerate()
577 .map(|(i, f)| Named {
578 name: format!("field{i}"),
579 value: f.clone(),
580 })
581 .collect::<Vec<_>>(),
582 Struct(fields) => fields.clone(),
583 Enum(variants) => {
584 if ((self.generator.config.enums.c_style
586 && !self.generator.config.enums.output_type.contains_key(name))
587 || self.generator.config.enums.output_type.get(name) == Some(&"enum"))
588 && variants.values().all(|f| f.value == VariantFormat::Unit)
589 {
590 self.output_enum_container(name, variants)?;
591 } else if (self.generator.config.enums.sealed
592 && !self.generator.config.enums.output_type.contains_key(name))
593 || self.generator.config.enums.output_type.get(name) == Some(&"sealed")
594 {
595 self.output_enum_class_container(name, variants, "sealed")?;
596 } else {
597 self.output_enum_class_container(name, variants, "abstract")?;
598 }
599 return Ok(());
600 }
601 };
602 self.output_struct_or_variant_container(None, None, name, &fields)
603 }
604
605 fn output_struct_or_variant_container(
606 &mut self,
607 variant_base: Option<&str>,
608 variant_index: Option<u32>,
609 name: &str,
610 fields: &[Named<Format>],
611 ) -> Result<()> {
612 let field_count = fields.len();
613
614 writeln!(self.out)?;
616 self.output_comment(name)?;
617 if let Some(base) = variant_base {
618 writeln!(
619 self.out,
620 "@immutable\nclass {} extends {} {{",
621 self.quote_qualified_name(name),
622 base
623 )?;
624 } else {
625 writeln!(
626 self.out,
627 "@immutable\nclass {} {{",
628 self.quote_qualified_name(name)
629 )?;
630 }
631 self.enter_class(name);
632
633 writeln!(
635 self.out,
636 "const {}({}",
637 self.quote_qualified_name(name),
638 if !fields.is_empty() { "{" } else { "" }
639 )?;
640 self.out.indent();
641 for field in fields.iter() {
642 let field_name = self.quote_field(&field.name.to_mixed_case());
643 match &field.value {
644 Format::Option(_) => writeln!(self.out, "this.{field_name},")?,
645 _ => writeln!(self.out, "required this.{field_name},")?,
646 }
647 }
648 self.out.unindent();
649 if variant_base.is_some() {
650 writeln!(
651 self.out,
652 "{}) : super();",
653 if !fields.is_empty() { "}" } else { "" }
654 )?;
655 } else {
656 writeln!(self.out, "{});", if !fields.is_empty() { "}" } else { "" })?;
657 }
658
659 if self.generator.config.serialization {
660 if variant_index.is_none() {
662 writeln!(
663 self.out,
664 "\nstatic {} deserialize(BinaryDeserializer deserializer) {{",
665 self.quote_qualified_name(name)
666 )?;
667 } else {
668 writeln!(
669 self.out,
670 "\nstatic {} load(BinaryDeserializer deserializer) {{",
671 self.quote_qualified_name(name)
672 )?;
673 }
674
675 self.out.indent();
676 writeln!(self.out, "deserializer.increaseContainerDepth();")?;
677 writeln!(
678 self.out,
679 "final instance = {}(",
680 self.quote_qualified_name(name)
681 )?;
682 self.out.indent();
683 for field in fields {
684 writeln!(
685 self.out,
686 "{}: {},",
687 self.quote_field(&field.name.to_mixed_case()),
688 self.quote_deserialize(&field.value)
689 )?;
690 }
691 self.out.unindent();
692 writeln!(self.out, ");")?;
693 writeln!(self.out, "deserializer.decreaseContainerDepth();")?;
694 writeln!(self.out, "return instance;")?;
695 self.out.unindent();
696 writeln!(self.out, "}}")?;
697
698 if variant_index.is_none() {
699 for encoding in &self.generator.config.encodings {
700 self.output_class_deserialize_for_encoding(name, *encoding)?;
701 }
702 }
703 }
704
705 if !fields.is_empty() {
707 writeln!(self.out)?;
708 }
709 for field in fields {
710 writeln!(
711 self.out,
712 "final {} {};",
713 self.quote_type(&field.value),
714 self.quote_field(&field.name.to_mixed_case())
715 )?;
716 }
717 if !fields.is_empty() {
718 writeln!(self.out)?;
719
720 let cls_name = self.quote_qualified_name(name);
721 writeln!(self.out, "{cls_name} copyWith({{")?;
722 self.out.indent();
723 for field in fields {
724 let field_name = self.quote_field(&field.name.to_mixed_case());
725 let field_type = self.quote_type(&field.value);
726
727 match field.value {
728 Format::Option(_) => {
729 writeln!(self.out, "{field_type} Function()? {field_name},")?
730 }
731 _ => writeln!(self.out, "{field_type}? {field_name},")?,
732 }
733 }
734 self.out.unindent();
735 writeln!(self.out, "}}) {{")?;
736 self.out.indent();
737 writeln!(self.out, "return {cls_name}(")?;
738 self.out.indent();
739
740 for field in fields {
741 let field_name = self.quote_field(&field.name.to_mixed_case());
742
743 match field.value {
744 Format::Option(_) => writeln!(
745 self.out,
746 "{field_name}: {field_name} == null ? this.{field_name} : {field_name}(),"
747 )?,
748 _ => writeln!(self.out, "{field_name}: {field_name} ?? this.{field_name},")?,
749 }
750 }
751 self.out.unindent();
752 writeln!(self.out, ");")?;
753 self.out.unindent();
754 writeln!(self.out, "}}")?;
755 }
756
757 if self.generator.config.serialization {
759 writeln!(self.out, "\nvoid serialize(BinarySerializer serializer) {{",)?;
760 self.out.indent();
761 writeln!(self.out, "serializer.increaseContainerDepth();")?;
762 if let Some(index) = variant_index {
763 writeln!(self.out, "serializer.serializeVariantIndex({index});")?;
764 }
765 for field in fields {
766 writeln!(
767 self.out,
768 "{}",
769 self.quote_serialize_value(
770 &self.quote_field(&field.name.to_mixed_case()),
771 &field.value
772 )
773 )?;
774 }
775 writeln!(self.out, "serializer.decreaseContainerDepth();")?;
776 self.out.unindent();
777 writeln!(self.out, "}}")?;
778
779 if variant_index.is_none() {
780 for encoding in &self.generator.config.encodings {
781 self.output_class_serialize_for_encoding(*encoding)?;
782 }
783 }
784 }
785
786 write!(self.out, "\n@override")?;
788 write!(self.out, "\nbool operator ==(Object other) {{")?;
789 self.out.indent();
790
791 writeln!(self.out, "\nif (identical(this, other)) return true;")?;
792 writeln!(
793 self.out,
794 "if (other.runtimeType != runtimeType) return false;"
795 )?;
796 write!(self.out, "\nreturn other is {name}")?;
797
798 self.out.indent();
799 for field in fields.iter() {
800 let value = if let Format::Option(value) = &field.value {
803 value
804 } else {
805 &field.value
806 };
807
808 let stmt = match value {
809 Format::Seq(_) => {
810 format!(
811 "listEquals({0}, other.{0})",
812 self.quote_field(&field.name.to_mixed_case())
813 )
814 }
815 Format::TupleArray {
816 content: _,
817 size: _,
818 } => format!(
819 "listEquals({0}, other.{0})",
820 self.quote_field(&field.name.to_mixed_case())
821 ),
822 Format::Map { .. } => {
823 format!(
824 "mapEquals({0}, other.{0})",
825 self.quote_field(&field.name.to_mixed_case())
826 )
827 }
828 _ => format!(
829 "{0} == other.{0}",
830 self.quote_field(&field.name.to_mixed_case())
831 ),
832 };
833
834 write!(self.out, "\n&& {stmt}")?;
835 }
836 writeln!(self.out, ";")?;
837 self.out.unindent();
838
839 self.out.unindent();
840 writeln!(self.out, "}}")?;
841
842 write!(self.out, "\n@override")?;
844 if field_count == 0 {
845 writeln!(self.out, "\nint get hashCode => runtimeType.hashCode;")?;
846 } else if field_count == 1 {
847 writeln!(
848 self.out,
849 "\nint get hashCode => {}.hashCode;",
850 fields.first().unwrap().name.to_mixed_case()
851 )?;
852 } else {
853 let use_hash_all = field_count > 20;
854
855 if use_hash_all {
856 writeln!(self.out, "\nint get hashCode => Object.hashAll([")?;
857 } else {
858 writeln!(self.out, "\nint get hashCode => Object.hash(")?;
859 }
860
861 self.out.indent();
862 self.out.indent();
863 self.out.indent();
864
865 for field in fields {
866 writeln!(
867 self.out,
868 "{},",
869 self.quote_field(&field.name.to_mixed_case())
870 )?;
871 }
872
873 self.out.unindent();
874
875 if use_hash_all {
876 writeln!(self.out, "]);")?;
877 } else {
878 writeln!(self.out, ");")?;
879 }
880
881 self.out.unindent();
882 self.out.unindent();
883 }
884
885 writeln!(self.out, "\n@override\nString toString() {{")?;
887 self.out.indent();
888 writeln!(self.out, "String? fullString;")?;
889 writeln!(self.out, "\nassert(() {{")?;
890 self.out.indent();
891 writeln!(self.out, "fullString = '$runtimeType('")?;
892 self.out.indent();
893 for (index, field) in fields.iter().enumerate() {
894 if index == field_count - 1 {
895 writeln!(
896 self.out,
897 "'{0}: ${0}'",
898 self.quote_field(&field.name.to_mixed_case())
899 )?;
900 } else {
901 writeln!(
902 self.out,
903 "'{0}: ${0}, '",
904 self.quote_field(&field.name.to_mixed_case())
905 )?;
906 }
907 }
908 writeln!(self.out, "')';")?;
909 self.out.unindent();
910 writeln!(self.out, "return true;")?;
911 self.out.unindent();
912 writeln!(self.out, "}}());")?;
913 writeln!(self.out, "\nreturn fullString ?? '{name}';")?;
914 self.out.unindent();
915 writeln!(self.out, "}}")?;
916
917 self.out.unindent();
918 self.leave_class();
920 writeln!(self.out, "}}")
921 }
922
923 fn output_class_serialize_for_encoding(&mut self, encoding: Encoding) -> Result<()> {
924 writeln!(
925 self.out,
926 r#"
927Uint8List {0}Serialize() {{
928 final serializer = {1}Serializer();
929 serialize(serializer);
930 return serializer.bytes;
931}}"#,
932 encoding.name(),
933 encoding.name().to_camel_case(),
934 )
935 }
936
937 fn output_class_deserialize_for_encoding(
938 &mut self,
939 name: &str,
940 encoding: Encoding,
941 ) -> Result<()> {
942 writeln!(
943 self.out,
944 r#"
945static {klass} {encoding}Deserialize(Uint8List input) {{
946 final deserializer = {encoding_class}Deserializer(input);
947 final value = {static_class}.deserialize(deserializer);
948 if (deserializer.offset < input.length) {{
949 throw Exception('Some input bytes were not read');
950 }}
951 return value;
952}}"#,
953 klass = self.quote_qualified_name(name),
954 static_class = self.quote_qualified_name(&self.get_class(name)),
955 encoding = encoding.name(),
956 encoding_class = encoding.name().to_camel_case()
957 )
958 }
959
960 fn output_enum_container(
961 &mut self,
962 name: &str,
963 variants: &BTreeMap<u32, Named<VariantFormat>>,
964 ) -> Result<()> {
965 writeln!(self.out)?;
966 self.output_comment(name)?;
967 writeln!(self.out, "enum {} {{", self.quote_qualified_name(name))?;
968 self.enter_class(name);
969
970 for variant in variants.values() {
971 writeln!(
972 self.out,
973 "{},",
974 self.quote_field(&variant.name.to_mixed_case())
975 )?;
976 }
977
978 self.out.unindent();
979 writeln!(self.out, "}}\n")?;
980
981 if self.generator.config.serialization {
982 writeln!(
983 self.out,
984 "extension {name}Extension on {n} {{",
985 name = name,
986 n = self.quote_qualified_name(name)
987 )?;
988 self.out.indent();
989 write!(
990 self.out,
991 "static {} deserialize(BinaryDeserializer deserializer) {{",
992 self.quote_qualified_name(name)
993 )?;
994 self.out.indent();
995 writeln!(
996 self.out,
997 r#"
998final index = deserializer.deserializeVariantIndex();
999switch (index) {{"#,
1000 )?;
1001 self.out.indent();
1002 for (index, variant) in variants {
1003 writeln!(
1004 self.out,
1005 "case {}: return {}.{};",
1006 index,
1007 self.quote_qualified_name(name),
1008 self.quote_field(&variant.name.to_mixed_case()),
1009 )?;
1010 }
1011 writeln!(
1012 self.out,
1013 "default: throw Exception('Unknown variant index for {}: ' + index.toString());",
1014 self.quote_qualified_name(name),
1015 )?;
1016 self.out.unindent();
1017 writeln!(self.out, "}}")?;
1018 self.out.unindent();
1019 writeln!(self.out, "}}\n")?;
1020
1021 write!(self.out, "void serialize(BinarySerializer serializer) {{")?;
1022
1023 self.out.indent();
1024 writeln!(
1025 self.out,
1026 r#"
1027switch (this) {{"#,
1028 )?;
1029 self.out.indent();
1030 for (index, variant) in variants {
1031 writeln!(
1032 self.out,
1033 "case {}.{}: return serializer.serializeVariantIndex({});",
1034 self.quote_qualified_name(name),
1035 self.quote_field(&variant.name.to_mixed_case()),
1036 index,
1037 )?;
1038 }
1039 self.out.unindent();
1040 writeln!(self.out, "}}")?;
1041 self.out.unindent();
1042 writeln!(self.out, "}}")?;
1043
1044 for encoding in &self.generator.config.encodings {
1045 self.output_class_serialize_for_encoding(*encoding)?;
1046 self.output_class_deserialize_for_encoding(name, *encoding)?;
1047 }
1048 }
1049 self.out.unindent();
1050 self.out.unindent();
1051
1052 writeln!(self.out, "}}\n")?;
1053
1054 self.leave_class();
1055 Ok(())
1056 }
1057
1058 fn output_enum_class_container(
1059 &mut self,
1060 name: &str,
1061 variants: &BTreeMap<u32, Named<VariantFormat>>,
1062 class_type: &str,
1063 ) -> Result<()> {
1064 writeln!(self.out)?;
1065 self.output_comment(name)?;
1066 writeln!(
1067 self.out,
1068 "{} class {} {{",
1069 class_type,
1070 self.quote_qualified_name(name)
1071 )?;
1072 self.enter_class(name);
1073 writeln!(self.out, "const {}();", self.quote_qualified_name(name))?;
1074
1075 if self.generator.config.serialization {
1076 writeln!(self.out, "\nvoid serialize(BinarySerializer serializer);")?;
1077 write!(
1078 self.out,
1079 "\nstatic {} deserialize(BinaryDeserializer deserializer) {{",
1080 self.quote_qualified_name(name)
1081 )?;
1082 self.out.indent();
1083 writeln!(
1084 self.out,
1085 r#"
1086int index = deserializer.deserializeVariantIndex();
1087switch (index) {{"#,
1088 )?;
1089 self.out.indent();
1090 for (index, variant) in variants {
1091 writeln!(
1092 self.out,
1093 "case {}: return {}{}.load(deserializer);",
1094 index,
1095 self.quote_qualified_name(name).to_camel_case(),
1096 self.quote_field(&variant.name),
1097 )?;
1098 }
1099 writeln!(
1100 self.out,
1101 "default: throw Exception('Unknown variant index for {}: ' + index.toString());",
1102 self.quote_qualified_name(name),
1103 )?;
1104 self.out.unindent();
1105 writeln!(self.out, "}}")?;
1106 self.out.unindent();
1107 writeln!(self.out, "}}")?;
1108
1109 for encoding in &self.generator.config.encodings {
1110 self.output_class_serialize_for_encoding(*encoding)?;
1111 self.output_class_deserialize_for_encoding(name, *encoding)?;
1112 }
1113 }
1114 self.out.unindent();
1115 self.out.unindent();
1116
1117 writeln!(self.out, "}}\n")?;
1118
1119 self.output_variants(name, variants)?;
1120 self.leave_class();
1121 Ok(())
1122 }
1123
1124 fn output_variants(
1125 &mut self,
1126 base: &str,
1127 variants: &BTreeMap<u32, Named<VariantFormat>>,
1128 ) -> Result<()> {
1129 for (index, variant) in variants {
1130 self.output_variant(
1131 base,
1132 *index,
1133 &format!("{}{}", base, &variant.name),
1134 &variant.value,
1135 )?;
1136 }
1137 Ok(())
1138 }
1139
1140 fn output_variant(
1141 &mut self,
1142 base: &str,
1143 index: u32,
1144 name: &str,
1145 variant: &VariantFormat,
1146 ) -> Result<()> {
1147 use VariantFormat::*;
1148 let fields = match variant {
1149 Unit => Vec::new(),
1150 NewType(format) => vec![Named {
1151 name: "value".to_string(),
1152 value: format.as_ref().clone(),
1153 }],
1154 Tuple(formats) => formats
1155 .iter()
1156 .enumerate()
1157 .map(|(i, f)| Named {
1158 name: format!("field{i}"),
1159 value: f.clone(),
1160 })
1161 .collect(),
1162 Struct(fields) => fields.clone(),
1163 Variable(_) => panic!("incorrect value"),
1164 };
1165 self.output_struct_or_variant_container(
1166 Some(&self.quote_qualified_name(base)),
1167 Some(index),
1168 name,
1169 &fields,
1170 )
1171 }
1172
1173 fn output_comment(&mut self, name: &str) -> std::io::Result<()> {
1174 let mut path = self.current_namespace.clone();
1175 path.push(name.to_string());
1176 if let Some(doc) = self.generator.config.comments.get(&path) {
1177 let text = textwrap::indent(doc, "/// ").replace("\n\n", "\n///\n");
1178 write!(self.out, "{text}")?;
1179 }
1180 Ok(())
1181 }
1182}
1183
1184pub struct Installer {
1186 install_dir: PathBuf,
1187}
1188
1189impl Installer {
1190 pub fn new(install_dir: PathBuf) -> Self {
1191 Installer { install_dir }
1192 }
1193
1194 fn install_runtime(
1195 &self,
1196 source_dir: include_dir::Dir,
1197 path: &str,
1198 ) -> std::result::Result<(), Box<dyn std::error::Error>> {
1199 let dir_path = self.install_dir.join(path);
1200 std::fs::create_dir_all(&dir_path)?;
1201 for entry in source_dir.files() {
1202 let mut file = std::fs::File::create(dir_path.join(entry.path()))?;
1203 file.write_all(entry.contents())?;
1204 }
1205 Ok(())
1206 }
1207
1208 fn write_package(&self, install_dir: &Path, module_name: &str) -> Result<()> {
1209 let mut file = std::fs::File::create(install_dir.join("pubspec.yaml"))?;
1210 let mut out = IndentedWriter::new(&mut file, IndentConfig::Space(2));
1211 writeln!(
1212 &mut out,
1213 r#"name: {module_name}
1214
1215environment:
1216 sdk: '>=3.0.0 <4.0.0'
1217
1218dependencies:
1219 meta: ^1.0.0
1220 tuple: ^2.0.0
1221"#
1222 )?;
1223 Ok(())
1224 }
1225}
1226
1227impl crate::SourceInstaller for Installer {
1228 type Error = Box<dyn std::error::Error>;
1229
1230 fn install_module(
1231 &self,
1232 config: &CodeGeneratorConfig,
1233 registry: &Registry,
1234 ) -> std::result::Result<(), Self::Error> {
1235 let generator = CodeGenerator::new(config);
1236 generator.output(self.install_dir.clone(), registry)?;
1237
1238 if config.package_manifest {
1240 self.write_package(&self.install_dir, &config.module_name)?;
1241 }
1242
1243 std::fs::write(
1245 self.install_dir
1246 .join("lib")
1247 .join(format!("{}.dart", &config.module_name)),
1248 format!(
1249 "export 'src/{name}/{name}.dart';",
1250 name = &config.module_name
1251 ),
1252 )?;
1253
1254 Ok(())
1255 }
1256
1257 fn install_serde_runtime(&self) -> std::result::Result<(), Self::Error> {
1258 self.install_runtime(include_directory!("runtime/dart/serde"), "lib/src/serde")
1259 }
1260
1261 fn install_bincode_runtime(&self) -> std::result::Result<(), Self::Error> {
1262 self.install_runtime(
1263 include_directory!("runtime/dart/bincode"),
1264 "lib/src/bincode",
1265 )
1266 }
1267
1268 fn install_bcs_runtime(&self) -> std::result::Result<(), Self::Error> {
1269 self.install_runtime(include_directory!("runtime/dart/bcs"), "lib/src/bcs")
1270 }
1271}