mfmt/build/
builder.rs

1use super::{flatten, indent, line_suffix, offside, r#break, sequence, Document};
2use alloc::{alloc::Allocator, boxed::Box, str, vec::Vec};
3
4/// Document builder.
5#[derive(Clone, Debug)]
6pub struct Builder<A: Allocator> {
7    allocator: A,
8}
9
10impl<'a, A: Allocator + Clone + 'a> Builder<A> {
11    /// Creates a document builder.
12    pub fn new(allocator: A) -> Self {
13        Self { allocator }
14    }
15
16    /// Returns an allocator.
17    pub fn allocator(&self) -> &A {
18        &self.allocator
19    }
20
21    /// Breaks a document into multiple lines.
22    pub fn r#break(&self, value: impl Into<Document<'a>>) -> Document<'a> {
23        r#break(self.allocate(value.into()))
24    }
25
26    /// Flattens a document.
27    pub fn flatten(&self, value: impl Into<Document<'a>>) -> Document<'a> {
28        flatten(self.allocate(value.into()))
29    }
30
31    /// Indents a document by a level.
32    pub fn indent(&self, value: impl Into<Document<'a>>) -> Document<'a> {
33        indent(self.allocate(value.into()))
34    }
35
36    /// Creates a document indented to a current column.
37    pub fn offside(&self, value: impl Into<Document<'a>>, soft: bool) -> Document<'a> {
38        offside(self.allocate(value.into()), soft)
39    }
40
41    /// Creates a sequence of documents.
42    pub fn sequence(
43        &self,
44        values: impl IntoIterator<Item = impl Into<Document<'a>>>,
45    ) -> Document<'a> {
46        sequence(self.allocate_slice(values.into_iter().map(Into::into)))
47    }
48
49    /// Creates a concatenation of strings.
50    pub fn strings<'b>(&self, values: impl IntoIterator<Item = &'b str>) -> Document<'a> {
51        self.allocate_str(values).into()
52    }
53
54    /// Creates a set of line suffixes.
55    pub fn line_suffixes<'b>(&self, values: impl IntoIterator<Item = &'b str>) -> Document<'a> {
56        line_suffix(self.allocate_str(values))
57    }
58
59    /// Allocates a value.
60    pub fn allocate<T>(&self, value: T) -> &'a T {
61        Box::leak(Box::new_in(value, self.allocator.clone()))
62    }
63
64    /// Allocates a slice.
65    pub fn allocate_slice<T>(&self, values: impl IntoIterator<Item = T>) -> &'a [T] {
66        let mut vec = Vec::new_in(self.allocator.clone());
67
68        vec.extend(values);
69
70        Vec::leak(vec)
71    }
72
73    /// Allocates a string.
74    pub fn allocate_str<'b>(&self, values: impl IntoIterator<Item = &'b str>) -> &'a str {
75        let mut vec = Vec::new_in(self.allocator.clone());
76
77        for value in values {
78            vec.extend(value.as_bytes().iter().copied());
79        }
80
81        str::from_utf8(Vec::leak(vec)).expect("utf-8 string")
82    }
83}
84
85#[cfg(test)]
86mod tests {
87    use super::*;
88    use crate::offside;
89    use alloc::alloc::Global;
90
91    #[test]
92    fn build_offside() {
93        let builder = Builder::new(Global);
94
95        assert_eq!(builder.offside("foo", false), offside(&"foo".into(), false));
96    }
97}