mfmt/build/
builder.rs

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