Skip to main content

cl_ast/
fmt.rs

1//! The Conlang format extensions
2
3use std::fmt::{Display, Write};
4
5/// The default indentation string. Defaults to four spaces.
6const INDENTATION: &str = {
7    match option_env!("CONLANG_INDENT") {
8        Some(indent) => indent,
9        None => "    ",
10    }
11};
12
13impl<W: Write + ?Sized> FmtAdapter for W {}
14pub trait FmtAdapter: Write {
15    /// Indents by one level.
16    fn indent(&mut self) -> Indent<'_, Self> {
17        Indent::new(self, INDENTATION)
18    }
19
20    /// Pastes `indent` after each newline.
21    fn indent_with<I: Display>(&mut self, indent: I) -> Indent<'_, Self, I> {
22        Indent::new(self, indent)
23    }
24
25    /// Delimits a section with `open` and `close`.
26    fn delimit<O: Display, E: Display>(&mut self, open: O, close: E) -> Delimit<'_, Self, E> {
27        Delimit::new(self, open, close)
28    }
29
30    /// Delimits a section with `open` and `close`, raising the indent level within.
31    fn delimit_indented<O: Display, E: Display>(
32        &mut self,
33        open: O,
34        close: E,
35    ) -> DelimitIndent<'_, Self, E> {
36        DelimitIndent::new(self, open, close)
37    }
38
39    /// Formats bracketed lists of the kind (Item (Comma Item)*)?
40    #[inline]
41    fn list<Iter: IntoIterator<Item: Display>, Sep: Display>(
42        &mut self,
43        items: Iter,
44        sep: Sep,
45    ) -> std::fmt::Result {
46        self.list_wrap("", items, sep, "")
47    }
48
49    /// Wraps a list in `open` and `close`.
50    /// This differs from [`FmtAdapter::delimit`] because it prints nothing
51    /// if the list is empty.
52    fn list_wrap<Iter: IntoIterator<Item: Display>, Sep: Display, O: Display, E: Display>(
53        &mut self,
54        open: O,
55        items: Iter,
56        sep: Sep,
57        close: E,
58    ) -> std::fmt::Result {
59        let mut iter = items.into_iter();
60        let Some(item) = iter.next() else {
61            return Ok(());
62        };
63
64        write!(self, "{open}{item}")?;
65        for item in iter {
66            write!(self, "{sep}{item}")?;
67        }
68        write!(self, "{close}")
69    }
70}
71
72/// Pads text with leading indentation after every newline
73pub struct Indent<'f, F: Write + ?Sized, I: Display = &'static str> {
74    indent: I,
75    needs_indent: bool,
76    f: &'f mut F,
77}
78
79impl<'f, F: Write + ?Sized, I: Display> Indent<'f, F, I> {
80    pub const fn new(f: &'f mut F, indent: I) -> Self {
81        Indent { f, needs_indent: false, indent }
82    }
83
84    /// Gets mutable access to the inner [Write]-adapter
85    pub const fn inner(&mut self) -> &mut F {
86        self.f
87    }
88}
89
90impl<F: Write + ?Sized, I: Display> Write for Indent<'_, F, I> {
91    fn write_str(&mut self, s: &str) -> std::fmt::Result {
92        for s in s.split_inclusive('\n') {
93            if self.needs_indent {
94                write!(self.f, "{}", self.indent)?;
95            }
96            self.needs_indent = s.ends_with('\n');
97            self.f.write_str(s)?;
98        }
99        Ok(())
100    }
101    fn write_char(&mut self, c: char) -> std::fmt::Result {
102        if self.needs_indent {
103            write!(self.f, "{}", self.indent)?;
104        }
105        self.needs_indent = c == '\n';
106        self.f.write_char(c)
107    }
108}
109
110/// Prints delimiters around anything formatted with this. Implies [Indent]
111pub struct Delimit<'f, F: Write + ?Sized, E: Display = &'static str> {
112    /// The formatter
113    pub f: &'f mut F,
114    close: E,
115}
116
117impl<F: Write + ?Sized, E: Display> Delimit<'_, F, E> {
118    /// Gets mutable access to the inner [Write]-adapter
119    pub const fn inner(&mut self) -> &mut F {
120        self.f
121    }
122}
123
124impl<'f, F: Write + ?Sized, E: Display> Delimit<'f, F, E> {
125    pub fn new<O: Display>(f: &'f mut F, open: O, close: E) -> Self {
126        let _ = write!(f, "{open}");
127        Self { f, close }
128    }
129}
130
131impl<F: Write + ?Sized, E: Display> Drop for Delimit<'_, F, E> {
132    fn drop(&mut self) {
133        let Self { f, close, .. } = self;
134        let _ = write!(f, "{close}");
135    }
136}
137
138impl<F: Write + ?Sized, E: Display> Write for Delimit<'_, F, E> {
139    fn write_str(&mut self, s: &str) -> std::fmt::Result {
140        self.f.write_str(s)
141    }
142}
143
144/// Prints delimiters around anything formatted with this. Implies [Indent]
145pub struct DelimitIndent<'f, F: Write + ?Sized, E: Display = &'static str> {
146    f: Indent<'f, F>,
147    close: E,
148}
149
150impl<F: Write + ?Sized, E: Display> DelimitIndent<'_, F, E> {
151    /// Gets mutable access to the inner [Write]-adapter
152    pub const fn inner(&mut self) -> &mut F {
153        self.f.inner()
154    }
155}
156
157impl<'f, F: Write + ?Sized, E: Display> DelimitIndent<'f, F, E> {
158    pub fn new<O: Display>(f: &'f mut F, open: O, close: E) -> Self {
159        let mut f = f.indent();
160        let _ = write!(f, "{open}");
161        Self { f, close }
162    }
163}
164
165impl<F: Write + ?Sized, E: Display> Drop for DelimitIndent<'_, F, E> {
166    fn drop(&mut self) {
167        let Self { f: Indent { f, .. }, close, .. } = self;
168        let _ = write!(f, "{close}");
169    }
170}
171
172impl<F: Write + ?Sized, E: Display> Write for DelimitIndent<'_, F, E> {
173    fn write_str(&mut self, s: &str) -> std::fmt::Result {
174        self.f.write_str(s)
175    }
176}