Skip to main content

zerodds_idl/ast/
print.rs

1// SPDX-License-Identifier: Apache-2.0
2// Copyright 2026 ZeroDDS Contributors
3//! AST Pretty-Print (T5.3).
4//!
5//! Implementiert [`fmt::Display`] fuer alle AST-Wurzel-Typen. Output ist
6//! valides IDL — re-parsbar zu einem aequivalenten AST (Roundtrip via
7//! T5.6 abgesichert).
8//!
9//! # Format-Konvention
10//! - 4-Space Indent
11//! - Block-Bodies mit `{` auf gleicher Zeile, `}` allein-stehend
12//! - Annotations vor der annotierten Konstruktion, eine pro Zeile bei
13//!   Top-Level-Decls, inline bei Members/Params
14//! - Keine Leerzeilen zwischen Top-Level-Definitions (Roundtrip-Stabilitaet)
15
16use core::fmt::{self, Write};
17
18use crate::ast::types::*;
19
20const INDENT: &str = "    ";
21
22// ============================================================================
23// Hilfsfunktionen
24// ============================================================================
25
26fn write_indent(f: &mut fmt::Formatter<'_>, depth: usize) -> fmt::Result {
27    for _ in 0..depth {
28        f.write_str(INDENT)?;
29    }
30    Ok(())
31}
32
33fn write_inline_annotations(f: &mut fmt::Formatter<'_>, anns: &[Annotation]) -> fmt::Result {
34    for ann in anns {
35        write!(f, "{ann} ")?;
36    }
37    Ok(())
38}
39
40fn write_block_annotations(
41    f: &mut fmt::Formatter<'_>,
42    anns: &[Annotation],
43    depth: usize,
44) -> fmt::Result {
45    for ann in anns {
46        write_indent(f, depth)?;
47        writeln!(f, "{ann}")?;
48    }
49    Ok(())
50}
51
52// ============================================================================
53// Specification + Definition
54// ============================================================================
55
56impl fmt::Display for Specification {
57    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
58        for (i, def) in self.definitions.iter().enumerate() {
59            if i > 0 {
60                writeln!(f)?;
61            }
62            print_definition(f, def, 0)?;
63        }
64        Ok(())
65    }
66}
67
68/// zerodds-lint: recursion-depth 64 (Parser/AST-Walk; bounded by IDL nesting)
69fn print_definition(f: &mut fmt::Formatter<'_>, def: &Definition, depth: usize) -> fmt::Result {
70    match def {
71        Definition::Module(m) => print_module(f, m, depth),
72        Definition::Type(t) => {
73            print_type_decl(f, t, depth)?;
74            f.write_str(";\n")
75        }
76        Definition::Const(c) => {
77            write_block_annotations(f, &c.annotations, depth)?;
78            write_indent(f, depth)?;
79            print_const_decl(f, c)?;
80            f.write_str(";\n")
81        }
82        Definition::Except(e) => {
83            write_block_annotations(f, &e.annotations, depth)?;
84            print_except_decl(f, e, depth)?;
85            f.write_str(";\n")
86        }
87        Definition::Interface(i) => {
88            print_interface_dcl(f, i, depth)?;
89            f.write_str(";\n")
90        }
91        Definition::ValueBox(v) => {
92            write_block_annotations(f, &v.annotations, depth)?;
93            write_indent(f, depth)?;
94            write!(f, "valuetype {} ", v.name.text)?;
95            print_type_spec(f, &v.type_spec)?;
96            f.write_str(";\n")
97        }
98        Definition::ValueForward(v) => {
99            write_indent(f, depth)?;
100            writeln!(f, "valuetype {};", v.name.text)
101        }
102        Definition::Annotation(a) => print_annotation_dcl(f, a, depth),
103        Definition::ValueDef(_)
104        | Definition::TypeId(_)
105        | Definition::TypePrefix(_)
106        | Definition::Import(_)
107        | Definition::Component(_)
108        | Definition::Home(_)
109        | Definition::Event(_)
110        | Definition::Porttype(_)
111        | Definition::Connector(_)
112        | Definition::TemplateModule(_)
113        | Definition::TemplateModuleInst(_) => {
114            write_indent(f, depth)?;
115            // Pretty-Printing fuer CORBA-/CCM-/Template-Konstrukte ist
116            // Code-Gen-Material; hier nur Marker fuer Roundtrip-
117            // Diagnostik.
118            writeln!(f, "/* corba/ccm/template construct (round-trip stub) */")
119        }
120        Definition::VendorExtension(v) => {
121            write_indent(f, depth)?;
122            // Vendor-Erweiterungen koennen nicht generisch zurueck-
123            // gedruckt werden — wir markieren sie als Kommentar fuer
124            // Roundtrip-Diagnostik. Echte Vendor-spezifische Pretty-
125            // Printer kommen mit dem jeweiligen Vendor-Adapter (T6+).
126            writeln!(
127                f,
128                "/* vendor-extension {} (raw not preserved) */",
129                v.production_name
130            )
131        }
132    }
133}
134
135/// zerodds-lint: recursion-depth 64 (Parser/AST-Walk; bounded by IDL nesting)
136fn print_module(f: &mut fmt::Formatter<'_>, m: &ModuleDef, depth: usize) -> fmt::Result {
137    write_block_annotations(f, &m.annotations, depth)?;
138    write_indent(f, depth)?;
139    writeln!(f, "module {} {{", m.name.text)?;
140    for d in &m.definitions {
141        print_definition(f, d, depth + 1)?;
142    }
143    write_indent(f, depth)?;
144    f.write_str("};\n")
145}
146
147// ============================================================================
148// Type-Decl
149// ============================================================================
150
151fn print_type_decl(f: &mut fmt::Formatter<'_>, td: &TypeDecl, depth: usize) -> fmt::Result {
152    match td {
153        TypeDecl::Constr(c) => print_constr_type_decl(f, c, depth),
154        TypeDecl::Typedef(t) => {
155            write_block_annotations(f, &t.annotations, depth)?;
156            write_indent(f, depth)?;
157            f.write_str("typedef ")?;
158            print_type_spec(f, &t.type_spec)?;
159            f.write_str(" ")?;
160            for (i, d) in t.declarators.iter().enumerate() {
161                if i > 0 {
162                    f.write_str(", ")?;
163                }
164                print_declarator(f, d)?;
165            }
166            Ok(())
167        }
168    }
169}
170
171fn print_constr_type_decl(
172    f: &mut fmt::Formatter<'_>,
173    c: &ConstrTypeDecl,
174    depth: usize,
175) -> fmt::Result {
176    match c {
177        ConstrTypeDecl::Struct(s) => print_struct_dcl(f, s, depth),
178        ConstrTypeDecl::Union(u) => print_union_dcl(f, u, depth),
179        ConstrTypeDecl::Enum(e) => print_enum_def(f, e, depth),
180        ConstrTypeDecl::Bitset(b) => print_bitset_decl(f, b, depth),
181        ConstrTypeDecl::Bitmask(b) => print_bitmask_decl(f, b, depth),
182    }
183}
184
185// ============================================================================
186// Struct
187// ============================================================================
188
189fn print_struct_dcl(f: &mut fmt::Formatter<'_>, s: &StructDcl, depth: usize) -> fmt::Result {
190    match s {
191        StructDcl::Def(d) => {
192            write_block_annotations(f, &d.annotations, depth)?;
193            write_indent(f, depth)?;
194            write!(f, "struct {}", d.name.text)?;
195            if let Some(base) = &d.base {
196                write!(f, " : {base}")?;
197            }
198            f.write_str(" {")?;
199            if d.members.is_empty() {
200                f.write_str("}")?;
201            } else {
202                f.write_str("\n")?;
203                for m in &d.members {
204                    print_member(f, m, depth + 1)?;
205                }
206                write_indent(f, depth)?;
207                f.write_str("}")?;
208            }
209            Ok(())
210        }
211        StructDcl::Forward(d) => {
212            write_indent(f, depth)?;
213            write!(f, "struct {}", d.name.text)
214        }
215    }
216}
217
218fn print_member(f: &mut fmt::Formatter<'_>, m: &Member, depth: usize) -> fmt::Result {
219    write_indent(f, depth)?;
220    write_inline_annotations(f, &m.annotations)?;
221    print_type_spec(f, &m.type_spec)?;
222    f.write_str(" ")?;
223    for (i, d) in m.declarators.iter().enumerate() {
224        if i > 0 {
225            f.write_str(", ")?;
226        }
227        print_declarator(f, d)?;
228    }
229    f.write_str(";\n")
230}
231
232// ============================================================================
233// Union
234// ============================================================================
235
236fn print_union_dcl(f: &mut fmt::Formatter<'_>, u: &UnionDcl, depth: usize) -> fmt::Result {
237    match u {
238        UnionDcl::Def(d) => {
239            write_block_annotations(f, &d.annotations, depth)?;
240            write_indent(f, depth)?;
241            write!(f, "union {} switch (", d.name.text)?;
242            print_switch_type_spec(f, &d.switch_type)?;
243            f.write_str(") {")?;
244            if d.cases.is_empty() {
245                f.write_str("}")?;
246            } else {
247                f.write_str("\n")?;
248                for c in &d.cases {
249                    print_case(f, c, depth + 1)?;
250                }
251                write_indent(f, depth)?;
252                f.write_str("}")?;
253            }
254            Ok(())
255        }
256        UnionDcl::Forward(d) => {
257            write_indent(f, depth)?;
258            write!(f, "union {}", d.name.text)
259        }
260    }
261}
262
263fn print_switch_type_spec(f: &mut fmt::Formatter<'_>, s: &SwitchTypeSpec) -> fmt::Result {
264    match s {
265        SwitchTypeSpec::Integer(i) => print_integer_type(f, *i),
266        SwitchTypeSpec::Char => f.write_str("char"),
267        SwitchTypeSpec::Boolean => f.write_str("boolean"),
268        SwitchTypeSpec::Octet => f.write_str("octet"),
269        SwitchTypeSpec::Scoped(s) => write!(f, "{s}"),
270    }
271}
272
273fn print_case(f: &mut fmt::Formatter<'_>, c: &Case, depth: usize) -> fmt::Result {
274    write_indent(f, depth)?;
275    write_inline_annotations(f, &c.annotations)?;
276    for label in &c.labels {
277        match label {
278            CaseLabel::Default => f.write_str("default: ")?,
279            CaseLabel::Value(e) => {
280                f.write_str("case ")?;
281                print_const_expr(f, e)?;
282                f.write_str(": ")?;
283            }
284        }
285    }
286    print_element_spec(f, &c.element)?;
287    f.write_str(";\n")
288}
289
290fn print_element_spec(f: &mut fmt::Formatter<'_>, e: &ElementSpec) -> fmt::Result {
291    write_inline_annotations(f, &e.annotations)?;
292    print_type_spec(f, &e.type_spec)?;
293    f.write_str(" ")?;
294    print_declarator(f, &e.declarator)
295}
296
297// ============================================================================
298// Enum / Bitset / Bitmask
299// ============================================================================
300
301fn print_enum_def(f: &mut fmt::Formatter<'_>, e: &EnumDef, depth: usize) -> fmt::Result {
302    write_block_annotations(f, &e.annotations, depth)?;
303    write_indent(f, depth)?;
304    write!(f, "enum {} {{", e.name.text)?;
305    if e.enumerators.is_empty() {
306        f.write_str("}")?;
307    } else {
308        f.write_str("\n")?;
309        for (i, en) in e.enumerators.iter().enumerate() {
310            write_indent(f, depth + 1)?;
311            write_inline_annotations(f, &en.annotations)?;
312            f.write_str(&en.name.text)?;
313            if i + 1 < e.enumerators.len() {
314                f.write_str(",")?;
315            }
316            f.write_str("\n")?;
317        }
318        write_indent(f, depth)?;
319        f.write_str("}")?;
320    }
321    Ok(())
322}
323
324fn print_bitset_decl(f: &mut fmt::Formatter<'_>, b: &BitsetDecl, depth: usize) -> fmt::Result {
325    write_block_annotations(f, &b.annotations, depth)?;
326    write_indent(f, depth)?;
327    write!(f, "bitset {}", b.name.text)?;
328    if let Some(base) = &b.base {
329        write!(f, " : {base}")?;
330    }
331    f.write_str(" {")?;
332    if b.bitfields.is_empty() {
333        f.write_str("}")?;
334    } else {
335        f.write_str("\n")?;
336        for bf in &b.bitfields {
337            print_bitfield(f, bf, depth + 1)?;
338        }
339        write_indent(f, depth)?;
340        f.write_str("}")?;
341    }
342    Ok(())
343}
344
345fn print_bitfield(f: &mut fmt::Formatter<'_>, b: &Bitfield, depth: usize) -> fmt::Result {
346    write_indent(f, depth)?;
347    write_inline_annotations(f, &b.annotations)?;
348    f.write_str("bitfield<")?;
349    print_const_expr(f, &b.spec.width)?;
350    if let Some(dt) = &b.spec.dest_type {
351        f.write_str(", ")?;
352        print_primitive_type(f, *dt)?;
353    }
354    f.write_str(">")?;
355    if let Some(name) = &b.name {
356        write!(f, " {}", name.text)?;
357    }
358    f.write_str(";\n")
359}
360
361fn print_bitmask_decl(f: &mut fmt::Formatter<'_>, b: &BitmaskDecl, depth: usize) -> fmt::Result {
362    write_block_annotations(f, &b.annotations, depth)?;
363    write_indent(f, depth)?;
364    write!(f, "bitmask {} {{", b.name.text)?;
365    if b.values.is_empty() {
366        f.write_str("}")?;
367    } else {
368        f.write_str("\n")?;
369        for (i, v) in b.values.iter().enumerate() {
370            write_indent(f, depth + 1)?;
371            write_inline_annotations(f, &v.annotations)?;
372            f.write_str(&v.name.text)?;
373            if i + 1 < b.values.len() {
374                f.write_str(",")?;
375            }
376            f.write_str("\n")?;
377        }
378        write_indent(f, depth)?;
379        f.write_str("}")?;
380    }
381    Ok(())
382}
383
384// ============================================================================
385// Declarator
386// ============================================================================
387
388fn print_declarator(f: &mut fmt::Formatter<'_>, d: &Declarator) -> fmt::Result {
389    match d {
390        Declarator::Simple(n) => f.write_str(&n.text),
391        Declarator::Array(a) => {
392            f.write_str(&a.name.text)?;
393            for sz in &a.sizes {
394                f.write_str("[")?;
395                print_const_expr(f, sz)?;
396                f.write_str("]")?;
397            }
398            Ok(())
399        }
400    }
401}
402
403// ============================================================================
404// Const-Decl + ConstExpr
405// ============================================================================
406
407fn print_const_decl(f: &mut fmt::Formatter<'_>, c: &ConstDecl) -> fmt::Result {
408    f.write_str("const ")?;
409    print_const_type(f, &c.type_)?;
410    write!(f, " {} = ", c.name.text)?;
411    print_const_expr(f, &c.value)
412}
413
414/// zerodds-lint: recursion-depth 64 (Parser/AST-Walk; bounded by IDL nesting)
415fn print_annotation_dcl(
416    f: &mut fmt::Formatter<'_>,
417    a: &AnnotationDcl,
418    depth: usize,
419) -> fmt::Result {
420    write_indent(f, depth)?;
421    writeln!(f, "@annotation {} {{", a.name.text)?;
422    for ec in &a.embedded_consts {
423        write_indent(f, depth + 1)?;
424        print_const_decl(f, ec)?;
425        f.write_str(";\n")?;
426    }
427    for et in &a.embedded_types {
428        print_type_decl(f, et, depth + 1)?;
429        f.write_str(";\n")?;
430    }
431    for m in &a.members {
432        write_indent(f, depth + 1)?;
433        print_const_type(f, &m.type_spec)?;
434        write!(f, " {}", m.name.text)?;
435        if let Some(d) = &m.default {
436            f.write_str(" default ")?;
437            print_const_expr(f, d)?;
438        }
439        f.write_str(";\n")?;
440    }
441    write_indent(f, depth)?;
442    f.write_str("};\n")
443}
444
445fn print_const_type(f: &mut fmt::Formatter<'_>, t: &ConstType) -> fmt::Result {
446    match t {
447        ConstType::Integer(i) => print_integer_type(f, *i),
448        ConstType::Floating(fl) => print_floating_type(f, *fl),
449        ConstType::Char => f.write_str("char"),
450        ConstType::WideChar => f.write_str("wchar"),
451        ConstType::Boolean => f.write_str("boolean"),
452        ConstType::Octet => f.write_str("octet"),
453        ConstType::String { wide: false } => f.write_str("string"),
454        ConstType::String { wide: true } => f.write_str("wstring"),
455        ConstType::Fixed => f.write_str("fixed"),
456        ConstType::Scoped(s) => write!(f, "{s}"),
457    }
458}
459
460/// zerodds-lint: recursion-depth 64 (Parser/AST-Walk; bounded by IDL nesting)
461fn print_const_expr(f: &mut fmt::Formatter<'_>, e: &ConstExpr) -> fmt::Result {
462    match e {
463        ConstExpr::Literal(l) => f.write_str(&l.raw),
464        ConstExpr::Scoped(s) => write!(f, "{s}"),
465        ConstExpr::Unary { op, operand, .. } => {
466            f.write_char(unary_op_char(*op))?;
467            print_const_expr(f, operand)
468        }
469        ConstExpr::Binary { op, lhs, rhs, .. } => {
470            // Klammern fuer eindeutige Roundtrip-Lesung.
471            f.write_str("(")?;
472            print_const_expr(f, lhs)?;
473            write!(f, " {} ", binary_op_str(*op))?;
474            print_const_expr(f, rhs)?;
475            f.write_str(")")
476        }
477    }
478}
479
480const fn unary_op_char(op: UnaryOp) -> char {
481    match op {
482        UnaryOp::Plus => '+',
483        UnaryOp::Minus => '-',
484        UnaryOp::BitNot => '~',
485    }
486}
487
488const fn binary_op_str(op: BinaryOp) -> &'static str {
489    match op {
490        BinaryOp::Or => "|",
491        BinaryOp::Xor => "^",
492        BinaryOp::And => "&",
493        BinaryOp::Shl => "<<",
494        BinaryOp::Shr => ">>",
495        BinaryOp::Add => "+",
496        BinaryOp::Sub => "-",
497        BinaryOp::Mul => "*",
498        BinaryOp::Div => "/",
499        BinaryOp::Mod => "%",
500    }
501}
502
503// ============================================================================
504// Exception
505// ============================================================================
506
507fn print_except_decl(f: &mut fmt::Formatter<'_>, e: &ExceptDecl, depth: usize) -> fmt::Result {
508    write_indent(f, depth)?;
509    write!(f, "exception {} {{", e.name.text)?;
510    if e.members.is_empty() {
511        f.write_str("}")?;
512    } else {
513        f.write_str("\n")?;
514        for m in &e.members {
515            print_member(f, m, depth + 1)?;
516        }
517        write_indent(f, depth)?;
518        f.write_str("}")?;
519    }
520    Ok(())
521}
522
523// ============================================================================
524// Interface
525// ============================================================================
526
527fn print_interface_dcl(f: &mut fmt::Formatter<'_>, i: &InterfaceDcl, depth: usize) -> fmt::Result {
528    match i {
529        InterfaceDcl::Def(d) => {
530            write_block_annotations(f, &d.annotations, depth)?;
531            write_indent(f, depth)?;
532            print_interface_kind(f, d.kind)?;
533            write!(f, "interface {}", d.name.text)?;
534            if !d.bases.is_empty() {
535                f.write_str(" : ")?;
536                for (idx, b) in d.bases.iter().enumerate() {
537                    if idx > 0 {
538                        f.write_str(", ")?;
539                    }
540                    write!(f, "{b}")?;
541                }
542            }
543            f.write_str(" {")?;
544            if d.exports.is_empty() {
545                f.write_str("}")?;
546            } else {
547                f.write_str("\n")?;
548                for e in &d.exports {
549                    print_export(f, e, depth + 1)?;
550                }
551                write_indent(f, depth)?;
552                f.write_str("}")?;
553            }
554            Ok(())
555        }
556        InterfaceDcl::Forward(d) => {
557            write_indent(f, depth)?;
558            print_interface_kind(f, d.kind)?;
559            write!(f, "interface {}", d.name.text)
560        }
561    }
562}
563
564fn print_interface_kind(f: &mut fmt::Formatter<'_>, kind: InterfaceKind) -> fmt::Result {
565    match kind {
566        InterfaceKind::Plain => Ok(()),
567        InterfaceKind::Abstract => f.write_str("abstract "),
568        InterfaceKind::Local => f.write_str("local "),
569    }
570}
571
572fn print_export(f: &mut fmt::Formatter<'_>, e: &Export, depth: usize) -> fmt::Result {
573    match e {
574        Export::Op(o) => {
575            write_block_annotations(f, &o.annotations, depth)?;
576            write_indent(f, depth)?;
577            print_op_decl(f, o)?;
578            f.write_str(";\n")
579        }
580        Export::Attr(a) => {
581            write_block_annotations(f, &a.annotations, depth)?;
582            write_indent(f, depth)?;
583            print_attr_decl(f, a)?;
584            f.write_str(";\n")
585        }
586        Export::Type(t) => {
587            print_type_decl(f, t, depth)?;
588            f.write_str(";\n")
589        }
590        Export::Const(c) => {
591            write_block_annotations(f, &c.annotations, depth)?;
592            write_indent(f, depth)?;
593            print_const_decl(f, c)?;
594            f.write_str(";\n")
595        }
596        Export::Except(ex) => {
597            print_except_decl(f, ex, depth)?;
598            f.write_str(";\n")
599        }
600    }
601}
602
603fn print_op_decl(f: &mut fmt::Formatter<'_>, o: &OpDecl) -> fmt::Result {
604    if o.oneway {
605        f.write_str("oneway ")?;
606    }
607    match &o.return_type {
608        None => f.write_str("void")?,
609        Some(t) => print_type_spec(f, t)?,
610    }
611    write!(f, " {}(", o.name.text)?;
612    for (i, p) in o.params.iter().enumerate() {
613        if i > 0 {
614            f.write_str(", ")?;
615        }
616        print_param_decl(f, p)?;
617    }
618    f.write_str(")")?;
619    if !o.raises.is_empty() {
620        f.write_str(" raises (")?;
621        for (i, r) in o.raises.iter().enumerate() {
622            if i > 0 {
623                f.write_str(", ")?;
624            }
625            write!(f, "{r}")?;
626        }
627        f.write_str(")")?;
628    }
629    Ok(())
630}
631
632fn print_param_decl(f: &mut fmt::Formatter<'_>, p: &ParamDecl) -> fmt::Result {
633    write_inline_annotations(f, &p.annotations)?;
634    f.write_str(match p.attribute {
635        ParamAttribute::In => "in ",
636        ParamAttribute::Out => "out ",
637        ParamAttribute::InOut => "inout ",
638    })?;
639    print_type_spec(f, &p.type_spec)?;
640    write!(f, " {}", p.name.text)
641}
642
643fn print_attr_decl(f: &mut fmt::Formatter<'_>, a: &AttrDecl) -> fmt::Result {
644    if a.readonly {
645        f.write_str("readonly ")?;
646    }
647    f.write_str("attribute ")?;
648    print_type_spec(f, &a.type_spec)?;
649    write!(f, " {}", a.name.text)
650}
651
652// ============================================================================
653// Type-Spec
654// ============================================================================
655
656/// zerodds-lint: recursion-depth 64 (Parser/AST-Walk; bounded by IDL nesting)
657fn print_type_spec(f: &mut fmt::Formatter<'_>, t: &TypeSpec) -> fmt::Result {
658    match t {
659        TypeSpec::Primitive(p) => print_primitive_type(f, *p),
660        TypeSpec::Scoped(s) => write!(f, "{s}"),
661        TypeSpec::Sequence(s) => {
662            f.write_str("sequence<")?;
663            print_type_spec(f, &s.elem)?;
664            if let Some(b) = &s.bound {
665                f.write_str(", ")?;
666                print_const_expr(f, b)?;
667            }
668            f.write_str(">")
669        }
670        TypeSpec::String(s) => {
671            f.write_str(if s.wide { "wstring" } else { "string" })?;
672            if let Some(b) = &s.bound {
673                f.write_str("<")?;
674                print_const_expr(f, b)?;
675                f.write_str(">")?;
676            }
677            Ok(())
678        }
679        TypeSpec::Fixed(p) => {
680            f.write_str("fixed<")?;
681            print_const_expr(f, &p.digits)?;
682            f.write_str(", ")?;
683            print_const_expr(f, &p.scale)?;
684            f.write_str(">")
685        }
686        TypeSpec::Map(m) => {
687            f.write_str("map<")?;
688            print_type_spec(f, &m.key)?;
689            f.write_str(", ")?;
690            print_type_spec(f, &m.value)?;
691            if let Some(b) = &m.bound {
692                f.write_str(", ")?;
693                print_const_expr(f, b)?;
694            }
695            f.write_str(">")
696        }
697        TypeSpec::Any => f.write_str("any"),
698    }
699}
700
701fn print_primitive_type(f: &mut fmt::Formatter<'_>, p: PrimitiveType) -> fmt::Result {
702    match p {
703        PrimitiveType::Integer(i) => print_integer_type(f, i),
704        PrimitiveType::Floating(fl) => print_floating_type(f, fl),
705        PrimitiveType::Char => f.write_str("char"),
706        PrimitiveType::WideChar => f.write_str("wchar"),
707        PrimitiveType::Boolean => f.write_str("boolean"),
708        PrimitiveType::Octet => f.write_str("octet"),
709    }
710}
711
712fn print_integer_type(f: &mut fmt::Formatter<'_>, i: IntegerType) -> fmt::Result {
713    f.write_str(match i {
714        IntegerType::Short => "short",
715        IntegerType::Long => "long",
716        IntegerType::LongLong => "long long",
717        IntegerType::UShort => "unsigned short",
718        IntegerType::ULong => "unsigned long",
719        IntegerType::ULongLong => "unsigned long long",
720        IntegerType::Int8 => "int8",
721        IntegerType::Int16 => "int16",
722        IntegerType::Int32 => "int32",
723        IntegerType::Int64 => "int64",
724        IntegerType::UInt8 => "uint8",
725        IntegerType::UInt16 => "uint16",
726        IntegerType::UInt32 => "uint32",
727        IntegerType::UInt64 => "uint64",
728    })
729}
730
731fn print_floating_type(f: &mut fmt::Formatter<'_>, fl: FloatingType) -> fmt::Result {
732    f.write_str(match fl {
733        FloatingType::Float => "float",
734        FloatingType::Double => "double",
735        FloatingType::LongDouble => "long double",
736    })
737}
738
739// ============================================================================
740// ScopedName + Annotation (Display direkt)
741// ============================================================================
742
743impl fmt::Display for ScopedName {
744    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
745        if self.absolute {
746            f.write_str("::")?;
747        }
748        for (i, p) in self.parts.iter().enumerate() {
749            if i > 0 {
750                f.write_str("::")?;
751            }
752            f.write_str(&p.text)?;
753        }
754        Ok(())
755    }
756}
757
758impl fmt::Display for Annotation {
759    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
760        write!(f, "@{}", self.name)?;
761        match &self.params {
762            AnnotationParams::None => Ok(()),
763            AnnotationParams::Empty => f.write_str("()"),
764            AnnotationParams::Single(e) => {
765                f.write_str("(")?;
766                print_const_expr(f, e)?;
767                f.write_str(")")
768            }
769            AnnotationParams::Named(items) => {
770                f.write_str("(")?;
771                for (i, p) in items.iter().enumerate() {
772                    if i > 0 {
773                        f.write_str(", ")?;
774                    }
775                    write!(f, "{}=", p.name.text)?;
776                    print_const_expr(f, &p.value)?;
777                }
778                f.write_str(")")
779            }
780        }
781    }
782}
783
784#[cfg(test)]
785mod tests {
786    #![allow(clippy::expect_used, clippy::panic, clippy::unwrap_used)]
787
788    use crate::config::ParserConfig;
789    use crate::parser::parse;
790
791    fn print(src: &str) -> String {
792        let ast = parse(src, &ParserConfig::default()).expect("parse");
793        format!("{ast}")
794    }
795
796    /// Helper fuer Tests die CORBA-Konstrukte nutzen (oneway-op,
797    /// abstract/local interface, etc.) — schaltet alle Features an.
798    fn print_full(src: &str) -> String {
799        let cfg = ParserConfig::full_4_2();
800        let ast = parse(src, &cfg).expect("parse");
801        format!("{ast}")
802    }
803
804    #[test]
805    fn prints_empty_module() {
806        let out = print("module Empty {};");
807        assert!(out.contains("module Empty"), "got: {out}");
808        assert!(out.contains("};"));
809    }
810
811    #[test]
812    fn prints_struct_with_members() {
813        let out = print("struct P { long x; long y; };");
814        assert!(out.contains("struct P"), "got: {out}");
815        assert!(out.contains("long x;"));
816        assert!(out.contains("long y;"));
817    }
818
819    #[test]
820    fn prints_struct_with_inheritance() {
821        let out = print("struct D : Base { long x; };");
822        assert!(out.contains("struct D : Base"), "got: {out}");
823    }
824
825    #[test]
826    fn prints_typedef_sequence() {
827        let out = print("typedef sequence<long> Seq;");
828        assert!(out.contains("typedef sequence<long> Seq"), "got: {out}");
829    }
830
831    #[test]
832    fn prints_const_with_arithmetic() {
833        let out = print("const long V = 1 + 2 * 3;");
834        assert!(out.contains("const long V"), "got: {out}");
835    }
836
837    #[test]
838    fn prints_enum() {
839        let out = print("enum Color { RED, GREEN, BLUE };");
840        assert!(out.contains("enum Color"), "got: {out}");
841        assert!(out.contains("RED"));
842        assert!(out.contains("GREEN"));
843    }
844
845    #[test]
846    fn prints_annotated_struct() {
847        let out = print("@topic struct Foo { @key long id; };");
848        assert!(out.contains("@topic"), "got: {out}");
849        assert!(out.contains("@key long id"));
850    }
851
852    #[test]
853    fn prints_interface_with_op() {
854        let out = print("interface S { long add(in long a, in long b); };");
855        assert!(out.contains("interface S"), "got: {out}");
856        assert!(out.contains("long add"));
857        assert!(out.contains("in long a"));
858    }
859
860    #[test]
861    fn prints_oneway_op_with_raises() {
862        // `oneway`-Op ist CORBA-Konstrukt, gated via corba_oneway_op.
863        let out = print_full("interface S { oneway void log(in string m) raises (E); };");
864        assert!(out.contains("oneway void log"), "got: {out}");
865        assert!(out.contains("raises (E)"));
866    }
867
868    #[test]
869    fn prints_readonly_attribute() {
870        let out = print("interface C { readonly attribute long value; };");
871        assert!(out.contains("readonly attribute long value"), "got: {out}");
872    }
873
874    #[test]
875    fn prints_union() {
876        let out = print("union V switch (long) { case 1: long a; default: string b; };");
877        assert!(out.contains("union V switch (long)"), "got: {out}");
878        assert!(out.contains("case 1: long a"));
879        assert!(out.contains("default: string b"));
880    }
881
882    #[test]
883    fn prints_map() {
884        let out = print("typedef map<string, long> Idx;");
885        assert!(out.contains("map<string, long>"), "got: {out}");
886    }
887
888    #[test]
889    fn prints_bitset() {
890        let out = print("bitset F { bitfield<3> level; bitfield<1> on; };");
891        assert!(out.contains("bitset F"), "got: {out}");
892        assert!(out.contains("bitfield<3> level"));
893    }
894
895    #[test]
896    fn prints_bitmask() {
897        let out = print("bitmask P { READ, WRITE };");
898        assert!(out.contains("bitmask P"), "got: {out}");
899        assert!(out.contains("READ"));
900        assert!(out.contains("WRITE"));
901    }
902
903    #[test]
904    fn prints_value_box() {
905        let out = print("valuetype Name string;");
906        assert!(out.contains("valuetype Name string"), "got: {out}");
907    }
908
909    #[test]
910    fn prints_module_with_nested_struct() {
911        let out = print("module svc { struct Foo { long x; }; };");
912        assert!(out.contains("module svc"), "got: {out}");
913        assert!(out.contains("struct Foo"));
914    }
915
916    #[test]
917    fn prints_scoped_name() {
918        let out = print("typedef ::ns::T Alias;");
919        assert!(out.contains("::ns::T"), "got: {out}");
920    }
921
922    #[test]
923    fn prints_annotation_with_named_params() {
924        let out = print("struct S { @range(min=0, max=10) long x; };");
925        assert!(out.contains("@range(min=0, max=10)"), "got: {out}");
926    }
927}