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(&mut self, indent: &'static str) -> Indent<'_, Self> {
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<Item: Display, Sep: Display>(&mut self, items: &[Item], sep: Sep) -> std::fmt::Result {
42        self.list_wrap("", items, sep, "")
43    }
44
45    fn list_end<Item: Display, Sep: Display, End: Display>(
46        &mut self,
47        items: &[Item],
48        sep: Sep,
49        end: End,
50    ) -> std::fmt::Result {
51        self.list_wrap("", items, sep, end)
52    }
53
54    /// Wraps a list in `open` and `close`.
55    /// This differs from [`FmtAdapter::delimit`] because it prints nothing
56    /// if the list is empty.
57    fn list_wrap<Item: Display, Sep: Display, O: Display, E: Display>(
58        &mut self,
59        open: O,
60        mut items: &[Item],
61        sep: Sep,
62        close: E,
63    ) -> std::fmt::Result {
64        if items.is_empty() {
65            return Ok(());
66        }
67        write!(self, "{open}")?;
68        while let [pat, rest @ ..] = items {
69            write!(self, "{pat}")?;
70            if !rest.is_empty() {
71                write!(self, "{sep}")?;
72            }
73            items = rest;
74        }
75        write!(self, "{close}")
76    }
77}
78
79/// Pads text with leading indentation after every newline
80pub struct Indent<'f, F: Write + ?Sized> {
81    indent: &'static str,
82    needs_indent: bool,
83    f: &'f mut F,
84}
85
86impl<'f, F: Write + ?Sized> Indent<'f, F> {
87    pub const fn new(f: &'f mut F, indent: &'static str) -> Self {
88        Indent { f, needs_indent: false, indent }
89    }
90
91    /// Gets mutable access to the inner [Write]-adapter
92    pub const fn inner(&mut self) -> &mut F {
93        self.f
94    }
95}
96
97impl<F: Write + ?Sized> Write for Indent<'_, F> {
98    fn write_str(&mut self, s: &str) -> std::fmt::Result {
99        for s in s.split_inclusive('\n') {
100            if self.needs_indent {
101                self.f.write_str(self.indent)?;
102            }
103            self.f.write_str(s)?;
104            self.needs_indent = s.ends_with('\n');
105        }
106        Ok(())
107    }
108    fn write_char(&mut self, c: char) -> std::fmt::Result {
109        if self.needs_indent {
110            self.f.write_str(self.indent)?;
111        }
112        self.needs_indent = c == '\n';
113        self.f.write_char(c)
114    }
115}
116
117/// Prints delimiters around anything formatted with this. Implies [Indent]
118pub struct Delimit<'f, F: Write + ?Sized, E: Display = &'static str> {
119    /// The formatter
120    pub f: &'f mut F,
121    close: E,
122}
123
124impl<F: Write + ?Sized, E: Display> Delimit<'_, F, E> {
125    /// Gets mutable access to the inner [Write]-adapter
126    pub const fn inner(&mut self) -> &mut F {
127        self.f
128    }
129}
130
131impl<'f, F: Write + ?Sized, E: Display> Delimit<'f, F, E> {
132    pub fn new<O: Display>(f: &'f mut F, open: O, close: E) -> Self {
133        let _ = write!(f, "{open}");
134        Self { f, close }
135    }
136}
137
138impl<F: Write + ?Sized, E: Display> Drop for Delimit<'_, F, E> {
139    fn drop(&mut self) {
140        let Self { f, close, .. } = self;
141        let _ = write!(f, "{close}");
142    }
143}
144
145impl<F: Write + ?Sized, E: Display> Write for Delimit<'_, F, E> {
146    fn write_str(&mut self, s: &str) -> std::fmt::Result {
147        self.f.write_str(s)
148    }
149}
150
151/// Prints delimiters around anything formatted with this. Implies [Indent]
152pub struct DelimitIndent<'f, F: Write + ?Sized, E: Display = &'static str> {
153    f: Indent<'f, F>,
154    close: E,
155}
156
157impl<F: Write + ?Sized, E: Display> DelimitIndent<'_, F, E> {
158    /// Gets mutable access to the inner [Write]-adapter
159    pub const fn inner(&mut self) -> &mut F {
160        self.f.inner()
161    }
162}
163
164impl<'f, F: Write + ?Sized, E: Display> DelimitIndent<'f, F, E> {
165    pub fn new<O: Display>(f: &'f mut F, open: O, close: E) -> Self {
166        let mut f = f.indent();
167        let _ = write!(f, "{open}");
168        Self { f, close }
169    }
170}
171
172impl<F: Write + ?Sized, E: Display> Drop for DelimitIndent<'_, F, E> {
173    fn drop(&mut self) {
174        let Self { f: Indent { f, .. }, close, .. } = self;
175        let _ = write!(f, "{close}");
176    }
177}
178
179impl<F: Write + ?Sized, E: Display> Write for DelimitIndent<'_, F, E> {
180    fn write_str(&mut self, s: &str) -> std::fmt::Result {
181        self.f.write_str(s)
182    }
183}