1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78

use delimiters::Delimiters;
use std::fmt::Write;

impl<W: Write + ?Sized> FmtAdapter for W {}
pub trait FmtAdapter: Write {
    fn indent(&mut self) -> Indent<Self> {
        Indent { f: self }
    }
    fn delimit(&mut self, delim: Delimiters) -> Delimit<Self> {
        Delimit::new(self, delim)
    }
}

/// Pads text with leading indentation after every newline
pub struct Indent<'f, F: Write + ?Sized> {
    f: &'f mut F,
}

impl<'f, F: Write + ?Sized> Write for Indent<'f, F> {
    fn write_str(&mut self, s: &str) -> std::fmt::Result {
        for s in s.split_inclusive('\n') {
            self.f.write_str(s)?;
            if s.ends_with('\n') {
                self.f.write_str("    ")?;
            }
        }
        Ok(())
    }
}

/// Prints [Delimiters] around anything formatted with this. Implies [Indent]
pub struct Delimit<'f, F: Write + ?Sized> {
    f: Indent<'f, F>,
    delim: Delimiters,
}
impl<'f, F: Write + ?Sized> Delimit<'f, F> {
    pub fn new(f: &'f mut F, delim: Delimiters) -> Self {
        let mut f = f.indent();
        let _ = f.write_str(delim.open);
        Self { f, delim }
    }
}
impl<'f, F: Write + ?Sized> Drop for Delimit<'f, F> {
    fn drop(&mut self) {
        let Self { f: Indent { f, .. }, delim } = self;
        let _ = f.write_str(delim.close);
    }
}

impl<'f, F: Write + ?Sized> Write for Delimit<'f, F> {
    fn write_str(&mut self, s: &str) -> std::fmt::Result {
        self.f.write_str(s)
    }
}

pub mod delimiters {
    #![allow(dead_code)]
    #[derive(Clone, Copy, Debug)]
    pub struct Delimiters {
        pub open: &'static str,
        pub close: &'static str,
    }
    /// Delimits with braces decorated with spaces  `" {\n"`, ..., `"\n}"`
    pub const SPACED_BRACES: Delimiters = Delimiters { open: " {\n", close: "\n}" };
    /// Delimits with braces on separate lines `{\n`, ..., `\n}`
    pub const BRACES: Delimiters = Delimiters { open: "{\n", close: "\n}" };
    /// Delimits with parentheses on separate lines `{\n`, ..., `\n}`
    pub const PARENS: Delimiters = Delimiters { open: "(\n", close: "\n)" };
    /// Delimits with square brackets on separate lines `{\n`, ..., `\n}`
    pub const SQUARE: Delimiters = Delimiters { open: "[\n", close: "\n]" };
    /// Delimits with braces on the same line `{ `, ..., ` }`
    pub const INLINE_BRACES: Delimiters = Delimiters { open: "{ ", close: " }" };
    /// Delimits with parentheses on the same line `( `, ..., ` )`
    pub const INLINE_PARENS: Delimiters = Delimiters { open: "(", close: ")" };
    /// Delimits with square brackets on the same line `[ `, ..., ` ]`
    pub const INLINE_SQUARE: Delimiters = Delimiters { open: "[", close: "]" };
}