1use std::collections::HashMap;
16use std::fs::File;
17use std::io::{self, Write};
18use std::path::Path;
19
20use ferogram_tl_parser::tl::{Category, Definition, ParameterType};
21
22use crate::grouper;
23use crate::metadata::Metadata;
24use crate::namegen as n;
25
26pub struct Config {
28 pub gen_name_for_id: bool,
30 pub deserializable_functions: bool,
32 pub impl_debug: bool,
34 pub impl_from_type: bool,
36 pub impl_from_enum: bool,
38 pub impl_serde: bool,
40 pub gen_field_accessors: bool,
43}
44
45impl Default for Config {
46 fn default() -> Self {
47 Self {
48 gen_name_for_id: false,
49 deserializable_functions: false,
50 impl_debug: true,
51 impl_from_type: true,
52 impl_from_enum: true,
53 impl_serde: false,
54 gen_field_accessors: true,
55 }
56 }
57}
58
59pub struct Outputs<W: Write> {
63 pub common: W,
65 pub types: W,
67 pub functions: W,
69 pub enums: W,
71}
72
73impl Outputs<File> {
74 pub fn from_dir(out_dir: &str) -> io::Result<Self> {
76 let p = Path::new(out_dir);
77 Ok(Self {
78 common: File::create(p.join("generated_common.rs"))?,
79 types: File::create(p.join("generated_types.rs"))?,
80 functions: File::create(p.join("generated_functions.rs"))?,
81 enums: File::create(p.join("generated_enums.rs"))?,
82 })
83 }
84}
85
86impl<W: Write> Outputs<W> {
87 pub fn flush(&mut self) -> io::Result<()> {
89 self.common.flush()?;
90 self.types.flush()?;
91 self.functions.flush()?;
92 self.enums.flush()
93 }
94}
95
96const BUILTIN_TYPES: &[&str] = &["Bool", "True"];
100
101fn is_builtin(ty_name: &str) -> bool {
102 BUILTIN_TYPES.contains(&ty_name)
103}
104
105pub fn generate<W: Write>(
111 defs: &[Definition],
112 config: &Config,
113 outputs: &mut Outputs<W>,
114) -> io::Result<()> {
115 let meta = Metadata::build(defs);
116
117 write_common(defs, config, &mut outputs.common)?;
118 write_types_mod(defs, config, &meta, &mut outputs.types)?;
119 write_functions_mod(defs, config, &meta, &mut outputs.functions)?;
120 write_enums_mod(defs, config, &meta, &mut outputs.enums)?;
121
122 Ok(())
123}
124
125fn write_common<W: Write>(defs: &[Definition], config: &Config, out: &mut W) -> io::Result<()> {
128 writeln!(out, "// @generated: do not edit by hand")?;
131 writeln!(out, "// Re-run the build script to regenerate.")?;
132 writeln!(out)?;
133 writeln!(out, "/// The API layer this code was generated from.")?;
134 writeln!(out, "pub const LAYER: i32 = 0; // update via build.rs")?;
135 writeln!(out)?;
136
137 if config.gen_name_for_id {
138 writeln!(out, "/// Returns the TL name for a known constructor ID.")?;
139 writeln!(
140 out,
141 "pub fn name_for_id(id: u32) -> Option<&'static str> {{"
142 )?;
143 writeln!(out, " match id {{")?;
144 for def in defs {
145 writeln!(
146 out,
147 " {:#010x} => Some(\"{}\"),",
148 def.id,
149 def.full_name()
150 )?;
151 }
152 writeln!(out, " _ => None,")?;
153 writeln!(out, " }}")?;
154 writeln!(out, "}}")?;
155 }
156
157 Ok(())
158}
159
160fn write_types_mod<W: Write>(
163 defs: &[Definition],
164 config: &Config,
165 meta: &Metadata,
166 out: &mut W,
167) -> io::Result<()> {
168 writeln!(out, "// @generated: do not edit by hand")?;
169 writeln!(out, "pub mod types {{")?;
170
171 let grouped = grouper::group_by_ns(defs, Category::Types);
172 let mut namespaces: Vec<&String> = grouped.keys().collect();
173 namespaces.sort();
174
175 for ns in namespaces {
176 let bucket = &grouped[ns];
177 let indent: String = if ns.is_empty() {
178 " ".to_owned()
179 } else {
180 writeln!(out, " pub mod {ns} {{")?;
181 " ".to_owned()
182 };
183
184 for def in bucket {
185 write_struct(out, &indent, def, meta, config)?;
186 write_identifiable(out, &indent, def)?;
187 write_struct_serializable(out, &indent, def, meta)?;
188 write_struct_deserializable(out, &indent, def, meta)?;
189 }
190
191 if !ns.is_empty() {
192 writeln!(out, " }}")?;
193 }
194 }
195
196 writeln!(out, "}}")
197}
198
199fn write_functions_mod<W: Write>(
200 defs: &[Definition],
201 config: &Config,
202 meta: &Metadata,
203 out: &mut W,
204) -> io::Result<()> {
205 writeln!(out, "// @generated: do not edit by hand")?;
206 writeln!(out, "pub mod functions {{")?;
207
208 let grouped = grouper::group_by_ns(defs, Category::Functions);
209 let mut namespaces: Vec<&String> = grouped.keys().collect();
210 namespaces.sort();
211
212 for ns in namespaces {
213 let bucket = &grouped[ns];
214 let indent: String = if ns.is_empty() {
215 " ".to_owned()
216 } else {
217 writeln!(out, " pub mod {ns} {{")?;
218 " ".to_owned()
219 };
220
221 for def in bucket {
222 write_struct(out, &indent, def, meta, config)?;
223 write_identifiable(out, &indent, def)?;
224 write_struct_serializable(out, &indent, def, meta)?;
225 if config.deserializable_functions {
226 write_struct_deserializable(out, &indent, def, meta)?;
227 }
228 write_remote_call(out, &indent, def, meta)?;
229 }
230
231 if !ns.is_empty() {
232 writeln!(out, " }}")?;
233 }
234 }
235
236 writeln!(out, "}}")
237}
238
239fn generic_list(def: &Definition, bounds: &str) -> String {
242 let mut params: Vec<&str> = Vec::new();
243 for p in &def.params {
244 if let ParameterType::Normal { ty, .. } = &p.ty
245 && ty.generic_ref
246 && !params.contains(&ty.name.as_str())
247 {
248 params.push(&ty.name);
249 }
250 }
251 if params.is_empty() {
252 String::new()
253 } else {
254 format!("<{}>", params.join(&format!("{bounds}, ")) + bounds)
255 }
256}
257
258fn write_struct<W: Write>(
259 out: &mut W,
260 indent: &str,
261 def: &Definition,
262 meta: &Metadata,
263 config: &Config,
264) -> io::Result<()> {
265 let kind = match def.category {
266 Category::Types => "constructor",
267 Category::Functions => "method",
268 };
269 writeln!(
270 out,
271 "\n{indent}/// [`{name}`](https://core.telegram.org/{kind}/{name})\n\
272 {indent}///\n\
273 {indent}/// Generated from:\n\
274 {indent}/// ```tl\n\
275 {indent}/// {def}\n\
276 {indent}/// ```",
277 name = def.full_name(),
278 )?;
279
280 if config.impl_debug {
281 writeln!(out, "{indent}#[derive(Debug)]")?;
282 }
283 if config.impl_serde {
284 writeln!(
285 out,
286 "{indent}#[derive(serde::Serialize, serde::Deserialize)]"
287 )?;
288 }
289 writeln!(out, "{indent}#[derive(Clone, PartialEq)]")?;
290 writeln!(
291 out,
292 "{indent}pub struct {}{} {{",
293 n::def_type_name(def),
294 generic_list(def, ""),
295 )?;
296
297 for param in &def.params {
298 match ¶m.ty {
299 ParameterType::Flags => {} ParameterType::Normal { .. } => {
301 writeln!(
302 out,
303 "{indent} pub {}: {},",
304 n::param_attr_name(param),
305 n::param_qual_name(param, meta),
306 )?;
307 }
308 }
309 }
310 writeln!(out, "{indent}}}")
311}
312
313fn write_identifiable<W: Write>(out: &mut W, indent: &str, def: &Definition) -> io::Result<()> {
314 let gl = generic_list(def, "");
315 writeln!(
316 out,
317 "{indent}impl{gl} crate::Identifiable for {}{gl} {{\n\
318 {indent} const CONSTRUCTOR_ID: u32 = {:#010x};\n\
319 {indent}}}",
320 n::def_type_name(def),
321 def.id,
322 )
323}
324
325fn write_struct_serializable<W: Write>(
326 out: &mut W,
327 indent: &str,
328 def: &Definition,
329 meta: &Metadata,
330) -> io::Result<()> {
331 let gl_decl = generic_list(def, ": crate::Serializable");
332 let gl_use = generic_list(def, "");
333
334 writeln!(
335 out,
336 "{indent}impl{gl_decl} crate::Serializable for {}{gl_use} {{",
337 n::def_type_name(def)
338 )?;
339
340 let underscore = if def.category == Category::Types && def.params.is_empty() {
341 "_"
342 } else {
343 ""
344 };
345 writeln!(
346 out,
347 "{indent} fn serialize(&self, {underscore}buf: &mut impl Extend<u8>) {{"
348 )?;
349
350 if def.category == Category::Functions {
351 writeln!(out, "{indent} use crate::Identifiable;")?;
352 writeln!(out, "{indent} Self::CONSTRUCTOR_ID.serialize(buf);")?;
353 }
354
355 for param in &def.params {
356 write_param_serialization(out, indent, def, meta, param)?;
357 }
358
359 writeln!(out, "{indent} }}")?;
360 writeln!(out, "{indent}}}")
361}
362
363fn write_param_serialization<W: Write>(
364 out: &mut W,
365 indent: &str,
366 def: &Definition,
367 meta: &Metadata,
368 param: &ferogram_tl_parser::tl::Parameter,
369) -> io::Result<()> {
370 use ParameterType::*;
371
372 match ¶m.ty {
373 Flags => {
374 if meta.is_unused_flag(def, param) {
375 writeln!(out, "{indent} 0u32.serialize(buf);")?;
376 return Ok(());
377 }
378 write!(out, "{indent} (")?;
380 let mut first = true;
381 for other in &def.params {
382 if let Normal {
383 flag: Some(fl), ty, ..
384 } = &other.ty
385 {
386 if fl.name != param.name {
387 continue;
388 }
389 if !first {
390 write!(out, " | ")?;
391 }
392 first = false;
393 if ty.name == "true" {
394 write!(
395 out,
396 "if self.{} {{ 1 << {} }} else {{ 0 }}",
397 n::param_attr_name(other),
398 fl.index
399 )?;
400 } else {
401 write!(
402 out,
403 "if self.{}.is_some() {{ 1 << {} }} else {{ 0 }}",
404 n::param_attr_name(other),
405 fl.index
406 )?;
407 }
408 }
409 }
410 if first {
411 write!(out, "0u32")?;
412 }
413 writeln!(out, ").serialize(buf);")?;
414 }
415 Normal { ty, flag } => {
416 let attr = n::param_attr_name(param);
417 if flag.is_some() {
418 if ty.name == "true" {
419 } else {
421 writeln!(
422 out,
423 "{indent} if let Some(v) = &self.{attr} {{ v.serialize(buf); }}"
424 )?;
425 }
426 } else {
427 writeln!(out, "{indent} self.{attr}.serialize(buf);")?;
428 }
429 }
430 }
431 Ok(())
432}
433
434fn write_struct_deserializable<W: Write>(
435 out: &mut W,
436 indent: &str,
437 def: &Definition,
438 meta: &Metadata,
439) -> io::Result<()> {
440 let gl_decl = generic_list(def, ": crate::Deserializable");
441 let gl_use = generic_list(def, "");
442
443 let buf_name = if def.params.is_empty() { "_buf" } else { "buf" };
446
447 writeln!(
448 out,
449 "{indent}impl{gl_decl} crate::Deserializable for {}{gl_use} {{",
450 n::def_type_name(def)
451 )?;
452 writeln!(
453 out,
454 "{indent} fn deserialize({buf_name}: crate::deserialize::Buffer) -> crate::deserialize::Result<Self> {{"
455 )?;
456
457 let struct_name = n::def_type_name(def);
459 if !def.params.is_empty() {
460 writeln!(
461 out,
462 "{indent} if crate::deserialize::tl_debug() {{ \
463 eprintln!(\"[TL] >> {}::deserialize pos={{}}\", buf.pos()); }}",
464 struct_name
465 )?;
466 }
467
468 for param in &def.params {
471 match ¶m.ty {
472 ParameterType::Flags => {
473 let fp_attr = n::param_attr_name(param);
474 writeln!(
475 out,
476 "{indent} if crate::deserialize::tl_debug() {{ \
477 eprintln!(\"[TL] {struct_name}.{fp_attr} (flags) pos={{}}\", buf.pos()); }}"
478 )?;
479 writeln!(
480 out,
481 "{indent} let _{fp_attr} = u32::deserialize(buf)?;"
482 )?;
483 writeln!(
484 out,
485 "{indent} if crate::deserialize::tl_debug() {{ \
486 eprintln!(\"[TL] {struct_name}.{fp_attr} = {{:#034b}} pos={{}}\", _{fp_attr}, buf.pos()); }}"
487 )?;
488 }
489 ParameterType::Normal { ty, flag } => {
490 let attr = n::param_attr_name(param);
491 writeln!(
493 out,
494 "{indent} if crate::deserialize::tl_debug() {{ \
495 eprintln!(\"[TL] {struct_name}.{attr} pos={{}}\", buf.pos()); }}"
496 )?;
497 if let Some(fl) = flag {
498 if ty.name == "true" {
499 writeln!(
500 out,
501 "{indent} let {attr} = (_{} & (1 << {})) != 0;",
502 fl.name, fl.index
503 )?;
504 } else {
505 writeln!(
506 out,
507 "{indent} let {attr} = if (_{} & (1 << {})) != 0 {{ Some({}::deserialize(buf)?) }} else {{ None }};",
508 fl.name,
509 fl.index,
510 n::type_item_path(ty, meta)
511 )?;
512 }
513 } else {
514 writeln!(
515 out,
516 "{indent} let {attr} = {}::deserialize(buf)?;",
517 n::type_item_path(ty, meta)
518 )?;
519 }
520 writeln!(
522 out,
523 "{indent} if crate::deserialize::tl_debug() {{ \
524 eprintln!(\"[TL] {struct_name}.{attr} done pos={{}}\", buf.pos()); }}"
525 )?;
526 }
527 }
528 }
529
530 if !def.params.is_empty() {
532 writeln!(
533 out,
534 "{indent} if crate::deserialize::tl_debug() {{ \
535 eprintln!(\"[TL] << {struct_name}::deserialize done pos={{}}\", buf.pos()); }}"
536 )?;
537 }
538
539 writeln!(out, "{indent} Ok(Self {{")?;
540 for param in &def.params {
541 if param.ty != ParameterType::Flags {
542 let attr = n::param_attr_name(param);
543 writeln!(out, "{indent} {attr},")?;
544 }
545 }
546 writeln!(out, "{indent} }})")?;
547 writeln!(out, "{indent} }}")?;
548 writeln!(out, "{indent}}}")
549}
550
551fn write_remote_call<W: Write>(
552 out: &mut W,
553 indent: &str,
554 def: &Definition,
555 meta: &Metadata,
556) -> io::Result<()> {
557 let gl_decl = generic_list(def, ": crate::Serializable + crate::Deserializable");
560 let gl_use = generic_list(def, "");
561 writeln!(
562 out,
563 "{indent}impl{gl_decl} crate::RemoteCall for {}{gl_use} {{",
564 n::def_type_name(def)
565 )?;
566 writeln!(
567 out,
568 "{indent} type Return = {};",
569 n::type_qual_name(&def.ty, meta)
570 )?;
571 writeln!(out, "{indent}}}")
572}
573
574fn write_enums_mod<W: Write>(
577 defs: &[Definition],
578 config: &Config,
579 meta: &Metadata,
580 out: &mut W,
581) -> io::Result<()> {
582 writeln!(out, "// @generated: do not edit by hand")?;
583 writeln!(out, "pub mod enums {{")?;
584
585 let grouped = grouper::group_types_by_ns(defs);
586 let mut keys: Vec<&Option<String>> = grouped.keys().collect();
587 keys.sort();
588
589 for key in keys {
590 let types = &grouped[key];
591 let indent = if let Some(ns) = key {
592 writeln!(out, " pub mod {ns} {{")?;
593 " ".to_owned()
594 } else {
595 " ".to_owned()
596 };
597
598 for ty in types.iter().filter(|t| !is_builtin(&t.name)) {
599 write_enum(out, &indent, ty, meta, config)?;
600 write_enum_serializable(out, &indent, ty, meta)?;
601 write_enum_deserializable(out, &indent, ty, meta)?;
602 if config.impl_from_type {
603 write_impl_from(out, &indent, ty, meta)?;
604 }
605 if config.impl_from_enum {
606 write_impl_try_from(out, &indent, ty, meta)?;
607 }
608 if config.gen_field_accessors {
609 write_enum_accessors(out, &indent, ty, meta)?;
610 }
611 }
612
613 if key.is_some() {
614 writeln!(out, " }}")?;
615 }
616 }
617
618 writeln!(out, "}}")
619}
620
621fn write_enum<W: Write>(
622 out: &mut W,
623 indent: &str,
624 ty: &ferogram_tl_parser::tl::Type,
625 meta: &Metadata,
626 config: &Config,
627) -> io::Result<()> {
628 writeln!(
629 out,
630 "\n{indent}/// [`{name}`](https://core.telegram.org/type/{name})",
631 name = n::type_name(ty)
632 )?;
633 if config.impl_debug {
634 writeln!(out, "{indent}#[derive(Debug)]")?;
635 }
636 if config.impl_serde {
637 writeln!(
638 out,
639 "{indent}#[derive(serde::Serialize, serde::Deserialize)]"
640 )?;
641 }
642 writeln!(out, "{indent}#[derive(Clone, PartialEq)]")?;
643 writeln!(out, "{indent}pub enum {} {{", n::type_name(ty))?;
644
645 for def in meta.defs_for_type(ty) {
646 let variant = n::def_variant_name(def);
647 if def.params.is_empty() {
648 writeln!(out, "{indent} {variant},")?;
649 } else if meta.is_recursive(def) {
650 writeln!(
651 out,
652 "{indent} {variant}(Box<{}>),",
653 n::def_qual_name(def)
654 )?;
655 } else {
656 writeln!(out, "{indent} {variant}({}),", n::def_qual_name(def))?;
657 }
658 }
659
660 writeln!(out, "{indent}}}")
661}
662
663fn write_enum_serializable<W: Write>(
664 out: &mut W,
665 indent: &str,
666 ty: &ferogram_tl_parser::tl::Type,
667 meta: &Metadata,
668) -> io::Result<()> {
669 writeln!(
670 out,
671 "{indent}impl crate::Serializable for {} {{",
672 n::type_name(ty)
673 )?;
674 writeln!(
675 out,
676 "{indent} fn serialize(&self, buf: &mut impl Extend<u8>) {{"
677 )?;
678 writeln!(out, "{indent} use crate::Identifiable;")?;
679 writeln!(out, "{indent} match self {{")?;
680
681 for def in meta.defs_for_type(ty) {
682 let variant = n::def_variant_name(def);
683 let bind = if def.params.is_empty() { "" } else { "(x)" };
684 writeln!(out, "{indent} Self::{variant}{bind} => {{")?;
685 writeln!(
686 out,
687 "{indent} {}::CONSTRUCTOR_ID.serialize(buf);",
688 n::def_qual_name(def)
689 )?;
690 if !def.params.is_empty() {
691 writeln!(out, "{indent} x.serialize(buf);")?;
692 }
693 writeln!(out, "{indent} }}")?;
694 }
695
696 writeln!(out, "{indent} }}")?;
697 writeln!(out, "{indent} }}")?;
698 writeln!(out, "{indent}}}")
699}
700
701fn write_enum_deserializable<W: Write>(
702 out: &mut W,
703 indent: &str,
704 ty: &ferogram_tl_parser::tl::Type,
705 meta: &Metadata,
706) -> io::Result<()> {
707 writeln!(
708 out,
709 "{indent}impl crate::Deserializable for {} {{",
710 n::type_name(ty)
711 )?;
712 writeln!(
713 out,
714 "{indent} fn deserialize(buf: crate::deserialize::Buffer) -> crate::deserialize::Result<Self> {{"
715 )?;
716 let enum_name = n::type_name(ty);
717 writeln!(out, "{indent} use crate::Identifiable;")?;
718 writeln!(out, "{indent} let id = u32::deserialize(buf)?;")?;
719 writeln!(
720 out,
721 "{indent} if crate::deserialize::tl_debug() {{ \
722 eprintln!(\"[TL] ENUM {enum_name} ctor={{:#010x}} pos={{}}\", id, buf.pos()); }}"
723 )?;
724 writeln!(out, "{indent} Ok(match id {{")?;
725
726 for def in meta.defs_for_type(ty) {
727 let variant = n::def_variant_name(def);
728 let qual = n::def_qual_name(def);
729 if def.params.is_empty() {
730 writeln!(
731 out,
732 "{indent} {qual}::CONSTRUCTOR_ID => Self::{variant},"
733 )?;
734 } else if meta.is_recursive(def) {
735 writeln!(
736 out,
737 "{indent} {qual}::CONSTRUCTOR_ID => Self::{variant}(Box::new({qual}::deserialize(buf)?)),"
738 )?;
739 } else {
740 writeln!(
741 out,
742 "{indent} {qual}::CONSTRUCTOR_ID => Self::{variant}({qual}::deserialize(buf)?),"
743 )?;
744 }
745 }
746
747 writeln!(
748 out,
749 "{indent} _ => return Err(crate::deserialize::Error::UnexpectedConstructor {{ id }}),"
750 )?;
751 writeln!(out, "{indent} }})")?;
752 writeln!(out, "{indent} }}")?;
753 writeln!(out, "{indent}}}")
754}
755
756fn write_impl_from<W: Write>(
757 out: &mut W,
758 indent: &str,
759 ty: &ferogram_tl_parser::tl::Type,
760 meta: &Metadata,
761) -> io::Result<()> {
762 for def in meta.defs_for_type(ty) {
763 let enum_name = n::type_name(ty);
764 let qual = n::def_qual_name(def);
765 let variant = n::def_variant_name(def);
766
767 writeln!(out, "{indent}impl From<{qual}> for {enum_name} {{")?;
768 let underscore = if def.params.is_empty() { "_" } else { "" };
769 writeln!(out, "{indent} fn from({underscore}x: {qual}) -> Self {{")?;
770 if def.params.is_empty() {
771 writeln!(out, "{indent} Self::{variant}")?;
772 } else if meta.is_recursive(def) {
773 writeln!(out, "{indent} Self::{variant}(Box::new(x))")?;
774 } else {
775 writeln!(out, "{indent} Self::{variant}(x)")?;
776 }
777 writeln!(out, "{indent} }}")?;
778 writeln!(out, "{indent}}}")?;
779 }
780 Ok(())
781}
782
783fn write_impl_try_from<W: Write>(
784 out: &mut W,
785 indent: &str,
786 ty: &ferogram_tl_parser::tl::Type,
787 meta: &Metadata,
788) -> io::Result<()> {
789 let enum_name = n::type_name(ty);
790 for def in meta.defs_for_type(ty) {
791 if def.params.is_empty() {
792 continue;
793 }
794 let qual = n::def_qual_name(def);
795 let variant = n::def_variant_name(def);
796
797 writeln!(out, "{indent}impl TryFrom<{enum_name}> for {qual} {{")?;
798 writeln!(out, "{indent} type Error = {enum_name};")?;
799 writeln!(out, "{indent} #[allow(unreachable_patterns)]")?;
800 writeln!(
801 out,
802 "{indent} fn try_from(v: {enum_name}) -> Result<Self, Self::Error> {{"
803 )?;
804 writeln!(out, "{indent} match v {{")?;
805 if meta.is_recursive(def) {
806 writeln!(
807 out,
808 "{indent} {enum_name}::{variant}(x) => Ok(*x),"
809 )?;
810 } else {
811 writeln!(
812 out,
813 "{indent} {enum_name}::{variant}(x) => Ok(x),"
814 )?;
815 }
816 writeln!(out, "{indent} other => Err(other),")?;
817 writeln!(out, "{indent} }}")?;
818 writeln!(out, "{indent} }}")?;
819 writeln!(out, "{indent}}}")?;
820 }
821 Ok(())
822}
823
824struct FieldAgg {
845 rust_type: Option<String>,
847 mismatched: bool,
849 presence: Vec<Option<bool>>,
852}
853
854fn type_is_generic(ty: &ferogram_tl_parser::tl::Type) -> bool {
857 ty.generic_ref
858 || ty
859 .generic_arg
860 .as_deref()
861 .map(type_is_generic)
862 .unwrap_or(false)
863}
864
865fn write_enum_accessors<W: Write>(
866 out: &mut W,
867 indent: &str,
868 ty: &ferogram_tl_parser::tl::Type,
869 meta: &Metadata,
870) -> io::Result<()> {
871 let defs = meta.defs_for_type(ty);
872 if defs.len() < 2 {
873 return Ok(());
874 }
875
876 let mut order: Vec<String> = Vec::new();
879 let mut fields: HashMap<String, FieldAgg> = HashMap::new();
880
881 for (i, def) in defs.iter().enumerate() {
882 for param in &def.params {
883 let ParameterType::Normal { ty: pty, flag } = ¶m.ty else {
884 continue;
885 };
886 if type_is_generic(pty) {
887 continue;
888 }
889
890 let field = n::param_attr_name(param);
891 let (rust_type, is_optional) = if flag.is_some() && pty.name == "true" {
892 ("bool".to_owned(), false)
893 } else {
894 (n::type_qual_name(pty, meta), flag.is_some())
895 };
896
897 let agg = fields.entry(field.clone()).or_insert_with(|| {
898 order.push(field.clone());
899 FieldAgg {
900 rust_type: None,
901 mismatched: false,
902 presence: vec![None; defs.len()],
903 }
904 });
905
906 match &agg.rust_type {
907 None => agg.rust_type = Some(rust_type),
908 Some(existing) if *existing != rust_type => agg.mismatched = true,
909 _ => {}
910 }
911 agg.presence[i] = Some(is_optional);
912 }
913 }
914
915 let mut emitted_header = false;
916
917 for field in &order {
918 let agg = &fields[field];
919 if agg.mismatched {
920 continue;
921 }
922 let Some(rust_type) = &agg.rust_type else {
923 continue;
924 };
925
926 let present_count = agg.presence.iter().filter(|p| p.is_some()).count();
928 if present_count < 2 {
929 continue;
930 }
931
932 let all_present = present_count == defs.len();
933 let any_optional = agg.presence.iter().any(|p| matches!(p, Some(true)));
934 let direct = all_present && !any_optional;
937
938 if !emitted_header {
939 writeln!(out, "\n{indent}impl {} {{", n::type_name(ty))?;
940 emitted_header = true;
941 }
942
943 let ret_ty = if direct {
944 format!("&{rust_type}")
945 } else {
946 format!("Option<&{rust_type}>")
947 };
948
949 writeln!(
950 out,
951 "{indent} /// Returns `{field}` if present in this variant."
952 )?;
953 writeln!(out, "{indent} pub fn {field}(&self) -> {ret_ty} {{")?;
954 writeln!(out, "{indent} match self {{")?;
955
956 for (i, def) in defs.iter().enumerate() {
957 let variant = n::def_variant_name(def);
958 let bind = if def.params.is_empty() { "" } else { "(x)" };
959
960 match agg.presence[i] {
961 Some(true) => writeln!(
962 out,
963 "{indent} Self::{variant}{bind} => x.{field}.as_ref(),"
964 )?,
965 Some(false) => {
966 let expr = if direct {
967 format!("&x.{field}")
968 } else {
969 format!("Some(&x.{field})")
970 };
971 writeln!(out, "{indent} Self::{variant}{bind} => {expr},")?
972 }
973 None => {} }
975 }
976
977 if !all_present {
978 writeln!(out, "{indent} _ => None,")?;
979 }
980
981 writeln!(out, "{indent} }}")?;
982 writeln!(out, "{indent} }}")?;
983 }
984
985 if emitted_header {
986 writeln!(out, "{indent}}}")?;
987 }
988
989 Ok(())
990}