Skip to main content

wgsl_parse/
syntax_display.rs

1use crate::{span::Spanned, syntax::*};
2use core::fmt;
3use std::fmt::{Display, Formatter};
4
5use itertools::Itertools;
6
7// unstable: https://doc.rust-lang.org/std/fmt/struct.FormatterFn.html
8struct FormatFn<F: (Fn(&mut Formatter) -> fmt::Result)>(F);
9
10impl<F: Fn(&mut Formatter) -> fmt::Result> Display for FormatFn<F> {
11    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
12        (self.0)(f)
13    }
14}
15
16impl<T: Display> Display for Spanned<T> {
17    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
18        self.node().fmt(f)
19    }
20}
21
22struct Indent<T: Display>(pub T);
23
24impl<T: Display> Display for Indent<T> {
25    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
26        let indent = "    ";
27        let inner_display = self.0.to_string();
28        let fmt = inner_display
29            .lines()
30            .format_with("\n", |l, f| f(&format_args!("{indent}{l}")));
31        write!(f, "{fmt}")?;
32        Ok(())
33    }
34}
35
36impl Display for TranslationUnit {
37    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
38        #[cfg(feature = "imports")]
39        if !self.imports.is_empty() {
40            for import in &self.imports {
41                writeln!(f, "{import}\n")?;
42            }
43        }
44        if !self.global_directives.is_empty() {
45            let directives = self.global_directives.iter().format("\n");
46            write!(f, "{directives}\n\n")?;
47        }
48        let declarations = self
49            .global_declarations
50            .iter()
51            .filter(|decl| !matches!(decl.node(), GlobalDeclaration::Void))
52            .format("\n\n");
53        writeln!(f, "{declarations}")
54    }
55}
56
57impl Display for Ident {
58    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
59        write!(f, "{}", self.name())
60    }
61}
62
63#[cfg(feature = "imports")]
64impl Display for ImportStatement {
65    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
66        #[cfg(feature = "attributes")]
67        write!(f, "{}", fmt_attrs(&self.attributes, false))?;
68        let content = &self.content;
69        if let Some(path) = &self.path {
70            write!(f, "import {path}::{content};")
71        } else {
72            write!(f, "import {content};")
73        }
74    }
75}
76
77#[cfg(feature = "imports")]
78impl Display for ModulePath {
79    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
80        match &self.origin {
81            PathOrigin::Absolute => write!(f, "package")?,
82            PathOrigin::Relative(0) => write!(f, "self")?,
83            PathOrigin::Relative(n) => write!(f, "{}", (0..*n).map(|_| "super").format("::"))?,
84            PathOrigin::Package(p) => write!(f, "{p}")?,
85        };
86        if !self.components.is_empty() {
87            write!(f, "::{}", self.components.iter().format("::"))?;
88        }
89        Ok(())
90    }
91}
92
93#[cfg(feature = "imports")]
94impl Display for Import {
95    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
96        if !self.path.is_empty() {
97            let path = self.path.iter().format("::");
98            write!(f, "{path}::")?;
99        }
100        let content = &self.content;
101        write!(f, "{content}")
102    }
103}
104
105#[cfg(feature = "imports")]
106impl Display for ImportContent {
107    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
108        match self {
109            ImportContent::Item(item) => {
110                write!(f, "{}", item.ident)?;
111                if let Some(rename) = &item.rename {
112                    write!(f, " as {rename}")?;
113                }
114                Ok(())
115            }
116            ImportContent::Collection(coll) => {
117                let coll = coll.iter().format(", ");
118                write!(f, "{{ {coll} }}")
119            }
120        }
121    }
122}
123
124impl Display for GlobalDirective {
125    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
126        match self {
127            GlobalDirective::Diagnostic(print) => write!(f, "{print}"),
128            GlobalDirective::Enable(print) => write!(f, "{print}"),
129            GlobalDirective::Requires(print) => write!(f, "{print}"),
130        }
131    }
132}
133
134impl Display for DiagnosticDirective {
135    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
136        #[cfg(feature = "attributes")]
137        write!(f, "{}", fmt_attrs(&self.attributes, false))?;
138        let severity = &self.severity;
139        let rule = &self.rule_name;
140        write!(f, "diagnostic ({severity}, {rule});")
141    }
142}
143
144impl Display for EnableDirective {
145    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
146        #[cfg(feature = "attributes")]
147        write!(f, "{}", fmt_attrs(&self.attributes, false))?;
148        let exts = self.extensions.iter().format(", ");
149        write!(f, "enable {exts};")
150    }
151}
152
153impl Display for RequiresDirective {
154    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
155        #[cfg(feature = "attributes")]
156        write!(f, "{}", fmt_attrs(&self.attributes, false))?;
157        let exts = self.extensions.iter().format(", ");
158        write!(f, "requires {exts};")
159    }
160}
161
162impl Display for GlobalDeclaration {
163    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
164        match self {
165            GlobalDeclaration::Void => write!(f, ";"),
166            GlobalDeclaration::Declaration(print) => write!(f, "{print}"),
167            GlobalDeclaration::TypeAlias(print) => write!(f, "{print}"),
168            GlobalDeclaration::Struct(print) => write!(f, "{print}"),
169            GlobalDeclaration::Function(print) => write!(f, "{print}"),
170            GlobalDeclaration::ConstAssert(print) => write!(f, "{print}"),
171            #[cfg(feature = "condcomp")]
172            GlobalDeclaration::Compound(print) => write!(f, "{print}"),
173        }
174    }
175}
176
177impl Display for Declaration {
178    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
179        write!(f, "{}", fmt_attrs(&self.attributes, false))?;
180        let kind = &self.kind;
181        let name = &self.ident;
182        let ty = self
183            .ty
184            .iter()
185            .format_with("", |ty, f| f(&format_args!(": {ty}")));
186        let init = self
187            .initializer
188            .iter()
189            .format_with("", |ty, f| f(&format_args!(" = {ty}")));
190        write!(f, "{kind} {name}{ty}{init};")
191    }
192}
193
194impl Display for DeclarationKind {
195    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
196        match self {
197            Self::Const => write!(f, "const"),
198            Self::Override => write!(f, "override"),
199            Self::Let => write!(f, "let"),
200            Self::Var(None) => write!(f, "var"),
201            Self::Var(Some((a_s, None))) => write!(f, "var<{a_s}>"),
202            Self::Var(Some((a_s, Some(a_m)))) => write!(f, "var<{a_s}, {a_m}>"),
203        }
204    }
205}
206
207impl Display for TypeAlias {
208    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
209        #[cfg(feature = "attributes")]
210        write!(f, "{}", fmt_attrs(&self.attributes, false))?;
211        let name = &self.ident;
212        let ty = &self.ty;
213        write!(f, "alias {name} = {ty};")
214    }
215}
216
217impl Display for Struct {
218    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
219        #[cfg(feature = "attributes")]
220        write!(f, "{}", fmt_attrs(&self.attributes, false))?;
221        let name = &self.ident;
222        let members = Indent(self.members.iter().format(",\n"));
223        write!(f, "struct {name} {{\n{members}\n}}")
224    }
225}
226
227impl Display for StructMember {
228    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
229        write!(f, "{}", fmt_attrs(&self.attributes, false))?;
230        let name = &self.ident;
231        let ty = &self.ty;
232        write!(f, "{name}: {ty}")
233    }
234}
235
236impl Display for Function {
237    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
238        write!(f, "{}", fmt_attrs(&self.attributes, false))?;
239        let name = &self.ident;
240        let params = self.parameters.iter().format(", ");
241        let ret_ty = self.return_type.iter().format_with("", |ty, f| {
242            f(&FormatFn(|f: &mut Formatter| {
243                write!(f, "-> ")?;
244                write!(f, "{}", fmt_attrs(&self.return_attributes, true))?;
245                write!(f, "{ty} ")?;
246                Ok(())
247            }))
248        });
249        let body = &self.body;
250        write!(f, "fn {name}({params}) {ret_ty}{body}")
251    }
252}
253
254impl Display for FormalParameter {
255    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
256        write!(f, "{}", fmt_attrs(&self.attributes, true))?;
257        let name = &self.ident;
258        let ty = &self.ty;
259        write!(f, "{name}: {ty}")
260    }
261}
262
263impl Display for ConstAssert {
264    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
265        #[cfg(feature = "attributes")]
266        write!(f, "{}", fmt_attrs(&self.attributes, false))?;
267        let expr = &self.expression;
268        write!(f, "const_assert {expr};",)
269    }
270}
271
272#[cfg(feature = "condcomp")]
273impl Display for CompoundGlobalDeclaration {
274    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
275        write!(f, "{}", fmt_attrs(&self.attributes, false))?;
276        let stmts = Indent(self.body.iter().format("\n"));
277        write!(f, "{{\n{stmts}\n}}")
278    }
279}
280
281impl Display for Attribute {
282    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
283        match self {
284            Attribute::Align(e1) => write!(f, "@align({e1})"),
285            Attribute::Binding(e1) => write!(f, "@binding({e1})"),
286            Attribute::BlendSrc(e1) => write!(f, "@blend_src({e1})"),
287            Attribute::Builtin(e1) => write!(f, "@builtin({e1})"),
288            Attribute::Const => write!(f, "@const"),
289            Attribute::Diagnostic(DiagnosticAttribute { severity, rule }) => {
290                write!(f, "@diagnostic({severity}, {rule})")
291            }
292            Attribute::Group(e1) => write!(f, "@group({e1})"),
293            Attribute::Id(e1) => write!(f, "@id({e1})"),
294            Attribute::Interpolate(InterpolateAttribute { ty, sampling }) => {
295                if let Some(sampling) = sampling {
296                    write!(f, "@interpolate({ty}, {sampling})")
297                } else {
298                    write!(f, "@interpolate({ty})")
299                }
300            }
301            Attribute::Invariant => write!(f, "@invariant"),
302            Attribute::Location(e1) => write!(f, "@location({e1})"),
303            Attribute::MustUse => write!(f, "@must_use"),
304            Attribute::Size(e1) => write!(f, "@size({e1})"),
305            Attribute::WorkgroupSize(WorkgroupSizeAttribute { x, y, z }) => {
306                let xyz = std::iter::once(x).chain(y).chain(z).format(", ");
307                write!(f, "@workgroup_size({xyz})")
308            }
309            Attribute::Vertex => write!(f, "@vertex"),
310            Attribute::Fragment => write!(f, "@fragment"),
311            Attribute::Compute => write!(f, "@compute"),
312            #[cfg(feature = "naga-ext")]
313            Attribute::Task => write!(f, "@task"),
314            #[cfg(feature = "naga-ext")]
315            Attribute::Payload(p) => write!(f, "@payload({p})"),
316            #[cfg(feature = "naga-ext")]
317            Attribute::Mesh(m) => write!(f, "@mesh({m})"),
318            #[cfg(feature = "naga-ext")]
319            Attribute::RayGeneration => write!(f, "@ray_generation"),
320            #[cfg(feature = "naga-ext")]
321            Attribute::AnyHit => write!(f, "@any_hit"),
322            #[cfg(feature = "naga-ext")]
323            Attribute::ClosestHit => write!(f, "@closest_hit"),
324            #[cfg(feature = "naga-ext")]
325            Attribute::Miss => write!(f, "@miss"),
326            #[cfg(feature = "naga-ext")]
327            Attribute::IncomingPayload(p) => write!(f, "@incoming_payload({p})"),
328            #[cfg(feature = "imports")]
329            Attribute::Publish => write!(f, "@publish"),
330            #[cfg(feature = "condcomp")]
331            Attribute::If(e1) => write!(f, "@if({e1})"),
332            #[cfg(feature = "condcomp")]
333            Attribute::Elif(e1) => write!(f, "@elif({e1})"),
334            #[cfg(feature = "condcomp")]
335            Attribute::Else => write!(f, "@else"),
336            #[cfg(feature = "generics")]
337            Attribute::Type(e1) => write!(f, "@type({e1})"),
338            #[cfg(feature = "naga-ext")]
339            Attribute::EarlyDepthTest(None) => write!(f, "@early_depth_test"),
340            #[cfg(feature = "naga-ext")]
341            Attribute::EarlyDepthTest(Some(e1)) => write!(f, "@early_depth_test({e1})"),
342            Attribute::Custom(custom) => {
343                let name = &custom.name;
344                let args = custom.arguments.iter().format_with("", |args, f| {
345                    f(&format_args!("({})", args.iter().format(", ")))
346                });
347                write!(f, "@{name}{args}")
348            }
349        }
350    }
351}
352
353#[cfg(feature = "generics")]
354impl Display for TypeConstraint {
355    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
356        let name = &self.ident;
357        let variants = self.variants.iter().format(" | ");
358        write!(f, "{name}, {variants}")
359    }
360}
361
362fn fmt_attrs(attrs: &[AttributeNode], inline: bool) -> impl fmt::Display + '_ {
363    FormatFn(move |f| {
364        let print = attrs.iter().format(" ");
365        let suffix = if attrs.is_empty() {
366            ""
367        } else if inline {
368            " "
369        } else {
370            "\n"
371        };
372        write!(f, "{print}{suffix}")
373    })
374}
375
376impl Display for Expression {
377    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
378        match self {
379            Expression::Literal(print) => write!(f, "{print}"),
380            Expression::Parenthesized(print) => {
381                write!(f, "{print}")
382            }
383            Expression::NamedComponent(print) => write!(f, "{print}"),
384            Expression::Indexing(print) => write!(f, "{print}"),
385            Expression::Unary(print) => write!(f, "{print}"),
386            Expression::Binary(print) => write!(f, "{print}"),
387            Expression::FunctionCall(print) => write!(f, "{print}"),
388            Expression::TypeOrIdentifier(print) => write!(f, "{print}"),
389        }
390    }
391}
392
393impl Display for LiteralExpression {
394    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
395        match self {
396            LiteralExpression::Bool(true) => write!(f, "true"),
397            LiteralExpression::Bool(false) => write!(f, "false"),
398            LiteralExpression::AbstractInt(num) => write!(f, "{num}"),
399            LiteralExpression::AbstractFloat(num) => write!(f, "{num:?}"), // using the Debug formatter to print the trailing .0 in floats representing integers. because format!("{}", 3.0f32) == "3"
400            LiteralExpression::I32(num) => write!(f, "{num}i"),
401            LiteralExpression::U32(num) => write!(f, "{num}u"),
402            LiteralExpression::F32(num) => write!(f, "{num}f"),
403            LiteralExpression::F16(num) => write!(f, "{num}h"),
404            #[cfg(feature = "naga-ext")]
405            LiteralExpression::I64(num) => write!(f, "{num}li"),
406            #[cfg(feature = "naga-ext")]
407            LiteralExpression::U64(num) => write!(f, "{num}lu"),
408            #[cfg(feature = "naga-ext")]
409            LiteralExpression::F64(num) => write!(f, "{num}lf"),
410        }
411    }
412}
413
414impl Display for ParenthesizedExpression {
415    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
416        let expr = &self.expression;
417        write!(f, "({expr})")
418    }
419}
420
421impl Display for NamedComponentExpression {
422    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
423        let base = &self.base;
424        let component = &self.component;
425        write!(f, "{base}.{component}")
426    }
427}
428
429impl Display for IndexingExpression {
430    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
431        let base = &self.base;
432        let index = &self.index;
433        write!(f, "{base}[{index}]")
434    }
435}
436
437impl Display for UnaryExpression {
438    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
439        let operator = &self.operator;
440        let operand = &self.operand;
441        write!(f, "{operator}{operand}")
442    }
443}
444
445impl Display for BinaryExpression {
446    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
447        let operator = &self.operator;
448        let left = &self.left;
449        let right = &self.right;
450        write!(f, "{left} {operator} {right}")
451    }
452}
453
454impl Display for FunctionCall {
455    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
456        let ty = &self.ty;
457        let args = self.arguments.iter().format(", ");
458        write!(f, "{ty}({args})")
459    }
460}
461
462impl Display for TypeExpression {
463    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
464        #[cfg(feature = "imports")]
465        if let Some(path) = &self.path {
466            write!(f, "{path}::")?;
467        }
468
469        let name = &self.ident;
470        let tplt = fmt_template(&self.template_args);
471        write!(f, "{name}{tplt}")
472    }
473}
474
475impl Display for TemplateArg {
476    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
477        let expr = &self.expression;
478        write!(f, "{expr}")
479    }
480}
481
482fn fmt_template(tplt: &Option<Vec<TemplateArg>>) -> impl fmt::Display + '_ {
483    tplt.iter().format_with("", |tplt, f| {
484        f(&format_args!("<{}>", tplt.iter().format(", ")))
485    })
486}
487
488impl Display for Statement {
489    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
490        match self {
491            Statement::Void => write!(f, ";"),
492            Statement::Compound(print) => write!(f, "{print}"),
493            Statement::Assignment(print) => write!(f, "{print}"),
494            Statement::Increment(print) => write!(f, "{print}"),
495            Statement::Decrement(print) => write!(f, "{print}"),
496            Statement::If(print) => write!(f, "{print}"),
497            Statement::Switch(print) => write!(f, "{print}"),
498            Statement::Loop(print) => write!(f, "{print}"),
499            Statement::For(print) => write!(f, "{print}"),
500            Statement::While(print) => write!(f, "{print}"),
501            Statement::Break(print) => write!(f, "{print}"),
502            Statement::Continue(print) => write!(f, "{print}"),
503            Statement::Return(print) => write!(f, "{print}"),
504            Statement::Discard(print) => write!(f, "{print}"),
505            Statement::FunctionCall(print) => write!(f, "{print}"),
506            Statement::ConstAssert(print) => write!(f, "{print}"),
507            Statement::Declaration(print) => write!(f, "{print}"),
508        }
509    }
510}
511
512impl Display for CompoundStatement {
513    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
514        write!(f, "{}", fmt_attrs(&self.attributes, false))?;
515        let stmts = Indent(
516            self.statements
517                .iter()
518                .filter(|stmt| !matches!(stmt.node(), Statement::Void))
519                .format("\n"),
520        );
521        write!(f, "{{\n{stmts}\n}}")
522    }
523}
524
525impl Display for AssignmentStatement {
526    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
527        #[cfg(feature = "attributes")]
528        write!(f, "{}", fmt_attrs(&self.attributes, false))?;
529        let operator = &self.operator;
530        let lhs = &self.lhs;
531        let rhs = &self.rhs;
532        write!(f, "{lhs} {operator} {rhs};")
533    }
534}
535
536impl Display for IncrementStatement {
537    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
538        #[cfg(feature = "attributes")]
539        write!(f, "{}", fmt_attrs(&self.attributes, false))?;
540        let expr = &self.expression;
541        write!(f, "{expr}++;")
542    }
543}
544
545impl Display for DecrementStatement {
546    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
547        #[cfg(feature = "attributes")]
548        write!(f, "{}", fmt_attrs(&self.attributes, false))?;
549        let expr = &self.expression;
550        write!(f, "{expr}--;")
551    }
552}
553
554impl Display for IfStatement {
555    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
556        write!(f, "{}", fmt_attrs(&self.attributes, false))?;
557        let if_clause = &self.if_clause;
558        write!(f, "{if_clause}")?;
559        for else_if_clause in self.else_if_clauses.iter() {
560            write!(f, "\n{else_if_clause}")?;
561        }
562        if let Some(else_clause) = &self.else_clause {
563            write!(f, "\n{else_clause}")?;
564        }
565        Ok(())
566    }
567}
568
569impl Display for IfClause {
570    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
571        let expr = &self.expression;
572        let stmt = &self.body;
573        write!(f, "if {expr} {stmt}")
574    }
575}
576
577impl Display for ElseIfClause {
578    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
579        #[cfg(feature = "attributes")]
580        write!(f, "{}", fmt_attrs(&self.attributes, false))?;
581        let expr = &self.expression;
582        let stmt = &self.body;
583        write!(f, "else if {expr} {stmt}")
584    }
585}
586
587impl Display for ElseClause {
588    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
589        #[cfg(feature = "attributes")]
590        write!(f, "{}", fmt_attrs(&self.attributes, false))?;
591        let stmt = &self.body;
592        write!(f, "else {stmt}")
593    }
594}
595
596impl Display for SwitchStatement {
597    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
598        write!(f, "{}", fmt_attrs(&self.attributes, false))?;
599        let expr = &self.expression;
600        let body_attrs = fmt_attrs(&self.body_attributes, false);
601        let clauses = Indent(self.clauses.iter().format("\n"));
602        write!(f, "switch {expr} {body_attrs}{{\n{clauses}\n}}")
603    }
604}
605
606impl Display for SwitchClause {
607    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
608        #[cfg(feature = "attributes")]
609        write!(f, "{}", fmt_attrs(&self.attributes, false))?;
610        let cases = self.case_selectors.iter().format(", ");
611        let body = &self.body;
612        write!(f, "case {cases} {body}")
613    }
614}
615
616impl Display for CaseSelector {
617    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
618        match self {
619            CaseSelector::Default => write!(f, "default"),
620            CaseSelector::Expression(expr) => {
621                write!(f, "{expr}")
622            }
623        }
624    }
625}
626
627impl Display for LoopStatement {
628    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
629        write!(f, "{}", fmt_attrs(&self.attributes, false))?;
630        let body_attrs = fmt_attrs(&self.body.attributes, false);
631        let stmts = Indent(
632            self.body
633                .statements
634                .iter()
635                .filter(|stmt| !matches!(stmt.node(), Statement::Void))
636                .format("\n"),
637        );
638        let continuing = self
639            .continuing
640            .iter()
641            .format_with("", |cont, f| f(&format_args!("{}\n", Indent(cont))));
642        write!(f, "loop {body_attrs}{{\n{stmts}\n{continuing}}}")
643    }
644}
645
646impl Display for ContinuingStatement {
647    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
648        #[cfg(feature = "attributes")]
649        write!(f, "{}", fmt_attrs(&self.attributes, false))?;
650        let body_attrs = fmt_attrs(&self.body.attributes, false);
651        let stmts = Indent(
652            self.body
653                .statements
654                .iter()
655                .filter(|stmt| !matches!(stmt.node(), Statement::Void))
656                .format("\n"),
657        );
658        let break_if = self
659            .break_if
660            .iter()
661            .format_with("", |stmt, f| f(&format_args!("{}\n", Indent(stmt))));
662        write!(f, "continuing {body_attrs}{{\n{stmts}\n{break_if}}}")
663    }
664}
665
666impl Display for BreakIfStatement {
667    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
668        #[cfg(feature = "attributes")]
669        write!(f, "{}", fmt_attrs(&self.attributes, false))?;
670        let expr = &self.expression;
671        write!(f, "break if {expr};")
672    }
673}
674
675impl Display for ForStatement {
676    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
677        write!(f, "{}", fmt_attrs(&self.attributes, false))?;
678        let mut init = self
679            .initializer
680            .as_ref()
681            .map(|stmt| format!("{stmt}"))
682            .unwrap_or_default();
683        if init.ends_with(';') {
684            init.pop();
685        }
686        let cond = self
687            .condition
688            .iter()
689            .format_with("", |expr, f| f(&format_args!("{expr}")));
690        let mut updt = self
691            .update
692            .as_ref()
693            .map(|stmt| format!("{stmt}"))
694            .unwrap_or_default();
695        if updt.ends_with(';') {
696            updt.pop();
697        }
698        let body = &self.body;
699        write!(f, "for ({init}; {cond}; {updt}) {body}")
700    }
701}
702
703impl Display for WhileStatement {
704    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
705        write!(f, "{}", fmt_attrs(&self.attributes, false))?;
706        let cond = &self.condition;
707        let body = &self.body;
708        write!(f, "while {cond} {body}")
709    }
710}
711
712impl Display for BreakStatement {
713    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
714        #[cfg(feature = "attributes")]
715        write!(f, "{}", fmt_attrs(&self.attributes, false))?;
716        write!(f, "break;")
717    }
718}
719
720impl Display for ContinueStatement {
721    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
722        #[cfg(feature = "attributes")]
723        write!(f, "{}", fmt_attrs(&self.attributes, false))?;
724        write!(f, "continue;")
725    }
726}
727
728impl Display for ReturnStatement {
729    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
730        #[cfg(feature = "attributes")]
731        write!(f, "{}", fmt_attrs(&self.attributes, false))?;
732        let expr = self
733            .expression
734            .iter()
735            .format_with("", |expr, f| f(&format_args!(" {expr}")));
736        write!(f, "return{expr};")
737    }
738}
739
740impl Display for DiscardStatement {
741    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
742        #[cfg(feature = "attributes")]
743        write!(f, "{}", fmt_attrs(&self.attributes, false))?;
744        write!(f, "discard;")
745    }
746}
747
748impl Display for FunctionCallStatement {
749    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
750        #[cfg(feature = "attributes")]
751        write!(f, "{}", fmt_attrs(&self.attributes, false))?;
752        let call = &self.call;
753        write!(f, "{call};")
754    }
755}
756
757#[cfg(test)]
758mod test {
759    #[cfg(feature = "imports")]
760    use crate::syntax::ModulePath;
761    use crate::syntax::{Ident, TypeExpression};
762
763    #[test]
764    fn type_expression_display() {
765        let expr = TypeExpression {
766            #[cfg(feature = "imports")]
767            path: None,
768            ident: Ident::new("foo".into()),
769            template_args: None,
770        };
771
772        assert_eq!(expr.to_string(), "foo");
773
774        let expr = TypeExpression {
775            #[cfg(feature = "imports")]
776            path: Some(ModulePath::new(
777                crate::syntax::PathOrigin::Absolute,
778                vec!["bar".into(), "qux".into()],
779            )),
780            ident: Ident::new("foo".into()),
781            template_args: None,
782        };
783
784        if cfg!(feature = "imports") {
785            assert_eq!(expr.to_string(), "package::bar::qux::foo");
786        } else {
787            assert_eq!(expr.to_string(), "foo");
788        }
789    }
790}