Skip to main content

microcad_lang_base/
code_display.rs

1// Copyright © 2026 The µcad authors <info@microcad.xyz>
2// SPDX-License-Identifier: AGPL-3.0-or-later
3
4/// Default code format context
5#[derive(derive_more::Deref, derive_more::DerefMut)]
6pub struct CodeFormatContext<'a> {
7    /// Current depth
8    pub depth: usize,
9
10    /// Writer.
11    #[deref]
12    #[deref_mut]
13    pub f: &'a mut dyn std::fmt::Write,
14}
15
16impl<'a> CodeFormatContext<'a> {
17    /// Create new code format context
18    pub fn new(f: &'a mut dyn std::fmt::Write) -> Self {
19        Self { depth: 0, f }
20    }
21
22    /// Write indent
23    pub fn indent(&mut self) -> std::fmt::Result {
24        let indent = self.depth * 4;
25        write!(self, "{:indent$}", "")
26    }
27
28    /// A nested block.
29    pub fn nest<F>(&mut self, f: F) -> std::fmt::Result
30    where
31        F: FnOnce(&mut Self) -> std::fmt::Result,
32    {
33        self.depth += 1;
34        let result = f(self);
35        self.depth -= 1;
36        result
37    }
38}
39
40/// Trait to display valid µcad source code.
41pub trait CodeDisplay<'a, Context = CodeFormatContext<'a>> {
42    /// Display µcad source code.
43    fn code_display(&self, f: &mut Context) -> std::fmt::Result;
44}
45
46/// If it can be Displayed, it can be CodeDisplayed.
47impl<'a, T> CodeDisplay<'a> for T
48where
49    T: std::fmt::Display,
50{
51    fn code_display(&self, ctx: &mut CodeFormatContext) -> std::fmt::Result {
52        write!(ctx, "{self}")
53    }
54}
55
56/// For statements or items that should be on their own indented lines
57pub struct CodeStack<'a, T>(pub &'a [T]);
58
59/// For inline items separated by commas (like function arguments)
60pub struct CodeList<'a, T>(pub &'a [T]);
61
62impl<'a, 'b, T> CodeDisplay<'a> for CodeList<'b, T>
63where
64    T: CodeDisplay<'a>,
65{
66    fn code_display(&self, f: &mut CodeFormatContext<'a>) -> std::fmt::Result {
67        self.0.iter().try_for_each(|item| {
68            item.code_display(f)?;
69            write!(f, ", ")
70        })
71    }
72}
73
74impl<'a, 'b, T> CodeDisplay<'a> for CodeStack<'b, T>
75where
76    T: CodeDisplay<'a>,
77{
78    fn code_display(&self, f: &mut CodeFormatContext<'a>) -> std::fmt::Result {
79        self.0.iter().try_for_each(|item| {
80            item.code_display(f)?;
81            writeln!(f)
82        })
83    }
84}
85
86/// Code display macro for a DSL.
87#[macro_export]
88macro_rules! code_display {
89    // Overload 2: The Body (Vertical/Braced)
90    // We open the brace, nest the formatter, and loop through the exprs
91    ($f:expr => { $($body:expr)* }) => {
92        {
93            writeln!($f, "{{")?;
94            $f.nest(|f| {
95                $(
96                    $body.code_display(f)?;
97                )*
98                Ok(())
99            })?;
100            $f.indent()?; write!($f, "}}")
101        }
102    };
103
104
105    // Overload 1: The Header (Horizontal/Inline formatting)
106    // Matches: f => "fmt", args...
107    ($f:expr => $($arg:expr)*) => {
108        {
109            $(
110                $arg.code_display($f)?;
111            )*
112            Ok(())
113        }
114    };
115
116}