preserves_schema/syntax/
block.rs

1//! A library for emitting pretty-formatted structured source code.
2//!
3//! The main entry points are [Formatter::to_string] and [Formatter::write], plus the utilities
4//! in the [macros] submodule.
5
6use std::fmt::Write;
7use std::str;
8
9/// Default width for pretty-formatting, in columns.
10pub const DEFAULT_WIDTH: usize = 80;
11
12/// All pretty-formattable items must implement this trait.
13pub trait Emittable: std::fmt::Debug {
14    /// Serializes `self`, as pretty-printed code, on `f`.
15    fn write_on(&self, f: &mut Formatter);
16}
17
18/// Tailoring of behaviour for [Vertical] groupings.
19#[derive(Clone, PartialEq, Eq)]
20pub enum VerticalMode {
21    Variable,
22    Normal,
23    ExtraNewline,
24}
25
26/// Vertical formatting for [Emittable]s.
27pub trait Vertical {
28    fn set_vertical_mode(&mut self, mode: VerticalMode);
29    fn write_vertically_on(&self, f: &mut Formatter);
30}
31
32/// Polymorphic [Emittable], used consistently in the API.
33pub type Item = std::rc::Rc<dyn Emittable>;
34
35/// A possibly-vertical sequence of items with item-separating and -terminating text.
36#[derive(Clone)]
37pub struct Sequence {
38    pub items: Vec<Item>,
39    pub vertical_mode: VerticalMode,
40    pub separator: &'static str,
41    pub terminator: &'static str,
42}
43
44/// A sequence of items, indented when formatted vertically, surrounded by opening and closing
45/// text.
46#[derive(Clone)]
47pub struct Grouping {
48    pub sequence: Sequence,
49    pub open: &'static str,
50    pub close: &'static str,
51}
52
53/// State needed for pretty-formatting of [Emittable]s.
54pub struct Formatter {
55    /// Number of available columns. Used to decide between horizontal and vertical layouts.
56    pub width: usize,
57    indent_delta: String,
58    current_indent: String,
59    /// Mutable output buffer. Accumulates emitted text during writing.
60    pub buffer: String,
61}
62
63impl Formatter {
64    /// Construct a Formatter using [DEFAULT_WIDTH] and a four-space indent.
65    pub fn new() -> Self {
66        Formatter {
67            width: DEFAULT_WIDTH,
68            indent_delta: "    ".to_owned(),
69            current_indent: "\n".to_owned(),
70            buffer: String::new(),
71        }
72    }
73
74    /// Construct a Formatter just like `self` but with an empty `buffer`.
75    pub fn copy_empty(&self) -> Formatter {
76        Formatter {
77            width: self.width,
78            indent_delta: self.indent_delta.clone(),
79            current_indent: self.current_indent.clone(),
80            buffer: String::new(),
81        }
82    }
83
84    /// Yields the indent size.
85    pub fn indent_size(self) -> usize {
86        self.indent_delta.len()
87    }
88
89    /// Updates the indent size.
90    pub fn set_indent_size(&mut self, n: usize) {
91        self.indent_delta = str::repeat(" ", n)
92    }
93
94    /// Accumulates a text serialization of `e` in `buffer`.
95    pub fn write<E: Emittable>(&mut self, e: E) {
96        e.write_on(self)
97    }
98
99    /// Emits a newline followed by indentation into `buffer`.
100    pub fn newline(&mut self) {
101        self.buffer.push_str(&self.current_indent)
102    }
103
104    /// Creates a default Formatter, uses it to [write][Formatter::write] `e`, and yields the
105    /// contents of its `buffer`.
106    pub fn to_string<E: Emittable>(e: E) -> String {
107        let mut f = Formatter::new();
108        f.write(e);
109        f.buffer
110    }
111
112    /// Calls `f` in a context where the indentation has been increased by
113    /// [Formatter::indent_size] spaces. Restores the indentation level after `f` returns.
114    /// Yields the result of the call to `f`.
115    pub fn with_indent<R, F: FnOnce(&mut Self) -> R>(&mut self, f: F) -> R {
116        let old_indent = self.current_indent.clone();
117        self.current_indent += &self.indent_delta;
118        let r = f(self);
119        self.current_indent = old_indent;
120        r
121    }
122}
123
124impl Default for Formatter {
125    fn default() -> Self {
126        Self::new()
127    }
128}
129
130impl Default for VerticalMode {
131    fn default() -> Self {
132        Self::Variable
133    }
134}
135
136//---------------------------------------------------------------------------
137
138impl Emittable for &str {
139    fn write_on(&self, f: &mut Formatter) {
140        f.buffer.push_str(self)
141    }
142}
143
144impl Emittable for String {
145    fn write_on(&self, f: &mut Formatter) {
146        f.write(self.as_str())
147    }
148}
149
150impl<'a, E: Emittable> Emittable for &'a Vec<E>
151where
152    &'a E: Emittable,
153{
154    fn write_on(&self, f: &mut Formatter) {
155        for e in self.iter() {
156            f.write(e)
157        }
158    }
159}
160
161impl Emittable for Sequence {
162    fn write_on(&self, f: &mut Formatter) {
163        if self.vertical_mode != VerticalMode::Variable {
164            self.write_vertically_on(f)
165        } else {
166            let mut need_sep = false;
167            for e in self.items.iter() {
168                if need_sep {
169                    self.separator.write_on(f)
170                } else {
171                    need_sep = true
172                }
173                e.write_on(f)
174            }
175            if !self.items.is_empty() {
176                self.terminator.write_on(f)
177            }
178        }
179    }
180}
181
182impl Vertical for Sequence {
183    fn set_vertical_mode(&mut self, vertical_mode: VerticalMode) {
184        self.vertical_mode = vertical_mode;
185    }
186
187    fn write_vertically_on(&self, f: &mut Formatter) {
188        let mut i = self.items.len();
189        let mut first = true;
190        for e in self.items.iter() {
191            if !first {
192                if self.vertical_mode == VerticalMode::ExtraNewline {
193                    f.write("\n");
194                }
195                f.newline();
196            }
197            first = false;
198            e.write_on(f);
199            let delim = if i == 1 {
200                self.terminator
201            } else {
202                self.separator
203            };
204            delim
205                .trim_end_matches(|c: char| c.is_whitespace() && c != '\n')
206                .write_on(f);
207            i = i - 1;
208        }
209    }
210}
211
212impl Emittable for Grouping {
213    fn write_on(&self, f: &mut Formatter) {
214        if self.sequence.vertical_mode != VerticalMode::Variable {
215            self.write_vertically_on(f)
216        } else {
217            let mut g = f.copy_empty();
218            self.open.write_on(&mut g);
219            g.write(&self.sequence);
220            self.close.write_on(&mut g);
221            let s = g.buffer;
222            if s.len() <= f.width {
223                f.write(&s)
224            } else {
225                self.write_vertically_on(f)
226            }
227        }
228    }
229}
230
231impl Vertical for Grouping {
232    fn set_vertical_mode(&mut self, vertical_mode: VerticalMode) {
233        self.sequence.set_vertical_mode(vertical_mode);
234    }
235
236    fn write_vertically_on(&self, f: &mut Formatter) {
237        self.open.write_on(f);
238        if !self.sequence.items.is_empty() {
239            f.with_indent(|f| {
240                f.newline();
241                self.sequence.write_vertically_on(f)
242            });
243            f.newline()
244        }
245        self.close.write_on(f);
246    }
247}
248
249impl<'a, E: Emittable> Emittable for &'a E {
250    fn write_on(&self, f: &mut Formatter) {
251        (*self).write_on(f)
252    }
253}
254
255impl Emittable for Item {
256    fn write_on(&self, f: &mut Formatter) {
257        (**self).write_on(f)
258    }
259}
260
261impl std::fmt::Debug for Sequence {
262    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
263        f.write_str(&Formatter::to_string(self))
264    }
265}
266
267impl std::fmt::Debug for Grouping {
268    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
269        f.write_str(&Formatter::to_string(self))
270    }
271}
272
273//---------------------------------------------------------------------------
274
275/// Escapes `s` by substituting `\\` for `\`, `\"` for `"`, and `\u{...}` for characters
276/// outside the range 32..126, inclusive.
277///
278/// This process is intended to generate literals compatible with `rustc`; see [the language
279/// reference on "Character and string
280/// literals"](https://doc.rust-lang.org/reference/tokens.html#character-and-string-literals).
281pub fn escape_string(s: &str) -> String {
282    let mut buf = String::new();
283    buf.push('"');
284    for c in s.chars() {
285        match c {
286            '\\' => buf.push_str("\\\\"),
287            '"' => buf.push_str("\\\""),
288            _ if c >= ' ' && c <= '~' => buf.push(c),
289            _ => write!(&mut buf, "\\u{{{:x}}}", c as i32).expect("no IO errors building a string"),
290        }
291    }
292    buf.push('"');
293    buf
294}
295
296/// Escapes `bs` into a Rust byte string literal, treating each byte as its ASCII equivalent
297/// except producing `\\` for 0x5c, `\"` for 0x22, and `\x..` for bytes outside the range
298/// 0x20..0x7e, inclusive.
299///
300/// This process is intended to generate literals compatible with `rustc`; see [the language
301/// reference on "Byte string
302/// literals"](https://doc.rust-lang.org/reference/tokens.html#byte-string-literals).
303pub fn escape_bytes(bs: &[u8]) -> String {
304    let mut buf = String::new();
305    buf.push_str("b\"");
306    for b in bs {
307        let c = *b as char;
308        match c {
309            '\\' => buf.push_str("\\\\"),
310            '"' => buf.push_str("\\\""),
311            _ if c >= ' ' && c <= '~' => buf.push(c),
312            _ => write!(&mut buf, "\\x{:02x}", b).expect("no IO errors building a string"),
313        }
314    }
315    buf.push('"');
316    buf
317}
318
319//---------------------------------------------------------------------------
320
321/// Utilities for constructing many useful kinds of [Sequence] and [Grouping].
322pub mod constructors {
323    use super::Emittable;
324    use super::Grouping;
325    use super::Item;
326    use super::Sequence;
327    use super::Vertical;
328    use super::VerticalMode;
329
330    /// Produces a polymorphic, reference-counted [Item] from some generic [Emittable].
331    pub fn item<E: 'static + Emittable>(i: E) -> Item {
332        std::rc::Rc::new(i)
333    }
334
335    /// *a*`::`*b*`::`*...*`::`*z*
336    pub fn name(pieces: Vec<Item>) -> Sequence {
337        Sequence {
338            items: pieces,
339            vertical_mode: VerticalMode::default(),
340            separator: "::",
341            terminator: "",
342        }
343    }
344
345    /// *ab...z* (directly adjacent, no separators or terminators)
346    pub fn seq(items: Vec<Item>) -> Sequence {
347        Sequence {
348            items: items,
349            vertical_mode: VerticalMode::default(),
350            separator: "",
351            terminator: "",
352        }
353    }
354
355    /// *a*`, `*b*`, `*...*`, `*z*
356    pub fn commas(items: Vec<Item>) -> Sequence {
357        Sequence {
358            items: items,
359            vertical_mode: VerticalMode::default(),
360            separator: ", ",
361            terminator: "",
362        }
363    }
364
365    /// `(`*a*`, `*b*`, `*...*`, `*z*`)`
366    pub fn parens(items: Vec<Item>) -> Grouping {
367        Grouping {
368            sequence: commas(items),
369            open: "(",
370            close: ")",
371        }
372    }
373
374    /// `[`*a*`, `*b*`, `*...*`, `*z*`]`
375    pub fn brackets(items: Vec<Item>) -> Grouping {
376        Grouping {
377            sequence: commas(items),
378            open: "[",
379            close: "]",
380        }
381    }
382
383    /// `<`*a*`, `*b*`, `*...*`, `*z*`>`
384    pub fn anglebrackets(items: Vec<Item>) -> Grouping {
385        Grouping {
386            sequence: commas(items),
387            open: "<",
388            close: ">",
389        }
390    }
391
392    /// `{`*a*`, `*b*`, `*...*`, `*z*`}`
393    pub fn braces(items: Vec<Item>) -> Grouping {
394        Grouping {
395            sequence: commas(items),
396            open: "{",
397            close: "}",
398        }
399    }
400
401    /// `{`*a*` `*b*` `*...*` `*z*`}`
402    pub fn block(items: Vec<Item>) -> Grouping {
403        Grouping {
404            sequence: Sequence {
405                items: items,
406                vertical_mode: VerticalMode::default(),
407                separator: " ",
408                terminator: "",
409            },
410            open: "{",
411            close: "}",
412        }
413    }
414
415    /// As [block], but always vertical
416    pub fn codeblock(items: Vec<Item>) -> Grouping {
417        vertical(false, block(items))
418    }
419
420    /// `{`*a*`; `*b*`; `*...*`; `*z*`}`
421    pub fn semiblock(items: Vec<Item>) -> Grouping {
422        Grouping {
423            sequence: Sequence {
424                items: items,
425                vertical_mode: VerticalMode::default(),
426                separator: "; ",
427                terminator: "",
428            },
429            open: "{",
430            close: "}",
431        }
432    }
433
434    /// Overrides `v` to be always vertical.
435    ///
436    /// If `spaced` is true, inserts an extra newline between items.
437    pub fn vertical<V: Vertical>(spaced: bool, mut v: V) -> V {
438        v.set_vertical_mode(if spaced {
439            VerticalMode::ExtraNewline
440        } else {
441            VerticalMode::Normal
442        });
443        v
444    }
445
446    /// Adds a layer of indentation to the given [Sequence].
447    pub fn indented(sequence: Sequence) -> Grouping {
448        Grouping {
449            sequence,
450            open: "",
451            close: "",
452        }
453    }
454}
455
456/// Ergonomic syntax for using the constructors in submodule [constructors]; see the
457/// documentation for the macros, which appears on the [page for the crate
458/// itself][crate#macros].
459pub mod macros {
460    /// `name!(`*a*`, `*b*`, `*...*`, `*z*`)` ⟶ *a*`::`*b*`::`*...*`::`*z*
461    ///
462    /// See [super::constructors::name].
463    #[macro_export]
464    macro_rules! name {
465        ($($item:expr),*) => {$crate::syntax::block::constructors::name(vec![$(std::rc::Rc::new($item)),*])}
466    }
467
468    /// `seq!(`*a*`, `*b*`, `*...*`, `*z*`)` ⟶ *ab...z*
469    ///
470    /// See [super::constructors::seq].
471    #[macro_export]
472    macro_rules! seq {
473        ($($item:expr),*) => {$crate::syntax::block::constructors::seq(vec![$(std::rc::Rc::new($item)),*])}
474    }
475
476    /// `commas!(`*a*`, `*b*`, `*...*`, `*z*`)` ⟶ *a*`, `*b*`, `*...*`, `*z*
477    ///
478    /// See [super::constructors::commas].
479    #[macro_export]
480    macro_rules! commas {
481        ($($item:expr),*) => {$crate::syntax::block::constructors::commas(vec![$(std::rc::Rc::new($item)),*])}
482    }
483
484    /// `parens!(`*a*`, `*b*`, `*...*`, `*z*`)` ⟶ `(`*a*`, `*b*`, `*...*`, `*z*`)`
485    ///
486    /// See [super::constructors::parens].
487    #[macro_export]
488    macro_rules! parens {
489        ($($item:expr),*) => {$crate::syntax::block::constructors::parens(vec![$(std::rc::Rc::new($item)),*])}
490    }
491
492    /// `brackets!(`*a*`, `*b*`, `*...*`, `*z*`)` ⟶ `[`*a*`, `*b*`, `*...*`, `*z*`]`
493    ///
494    /// See [super::constructors::brackets].
495    #[macro_export]
496    macro_rules! brackets {
497        ($($item:expr),*) => {$crate::syntax::block::constructors::brackets(vec![$(std::rc::Rc::new($item)),*])}
498    }
499
500    /// `anglebrackets!(`*a*`, `*b*`, `*...*`, `*z*`)` ⟶ `<`*a*`, `*b*`, `*...*`, `*z*`>`
501    ///
502    /// See [super::constructors::anglebrackets].
503    #[macro_export]
504    macro_rules! anglebrackets {
505        ($($item:expr),*) => {$crate::syntax::block::constructors::anglebrackets(vec![$(std::rc::Rc::new($item)),*])}
506    }
507
508    /// `braces!(`*a*`, `*b*`, `*...*`, `*z*`)` ⟶ `{`*a*`, `*b*`, `*...*`, `*z*`}`
509    ///
510    /// See [super::constructors::braces].
511    #[macro_export]
512    macro_rules! braces {
513        ($($item:expr),*) => {$crate::syntax::block::constructors::braces(vec![$(std::rc::Rc::new($item)),*])}
514    }
515
516    /// `block!(`*a*`, `*b*`, `*...*`, `*z*`)` ⟶ `{`*a*` `*b*` `*...*` `*z*`}`
517    ///
518    /// See [super::constructors::block].
519    #[macro_export]
520    macro_rules! block {
521        ($($item:expr),*) => {$crate::syntax::block::constructors::block(vec![$(std::rc::Rc::new($item)),*])}
522    }
523
524    /// As [`block`]`!`, but always vertical. See
525    /// [constructors::codeblock][super::constructors::codeblock].
526    #[macro_export]
527    macro_rules! codeblock {
528        ($($item:expr),*) => {$crate::syntax::block::constructors::codeblock(vec![$(std::rc::Rc::new($item)),*])}
529    }
530
531    /// `semiblock!(`*a*`, `*b*`, `*...*`, `*z*`)` ⟶ `{`*a*`; `*b*`; `*...*`; `*z*`}`
532    ///
533    /// See [super::constructors::semiblock].
534    #[macro_export]
535    macro_rules! semiblock {
536        ($($item:expr),*) => {$crate::syntax::block::constructors::semiblock(vec![$(std::rc::Rc::new($item)),*])}
537    }
538}