biome_console/
fmt.rs

1use std::{borrow::Cow, fmt, io, time::Duration};
2
3pub use crate::write::{Termcolor, Write, HTML};
4use crate::{markup, Markup, MarkupElement};
5
6/// A stack-allocated linked-list of [MarkupElement] slices
7#[derive(Clone, Copy)]
8pub enum MarkupElements<'a> {
9    Root,
10    Node(&'a Self, &'a [MarkupElement<'a>]),
11}
12
13impl<'a> MarkupElements<'a> {
14    /// Iterates on all the element slices depth-first
15    pub fn for_each(
16        &self,
17        func: &mut impl FnMut(&'a [MarkupElement]) -> io::Result<()>,
18    ) -> io::Result<()> {
19        if let Self::Node(parent, elem) = self {
20            parent.for_each(func)?;
21            func(elem)?;
22        }
23
24        Ok(())
25    }
26
27    /// Iterates on all the element slices breadth-first
28    pub fn for_each_rev(
29        &self,
30        func: &mut impl FnMut(&'a [MarkupElement]) -> io::Result<()>,
31    ) -> io::Result<()> {
32        if let Self::Node(parent, elem) = self {
33            func(elem)?;
34            parent.for_each(func)?;
35        }
36
37        Ok(())
38    }
39}
40
41/// The [Formatter] is the `biome_console` equivalent to [std::fmt::Formatter]:
42/// it's never constructed directly by consumers, and can only be used through
43/// the mutable reference passed to implementations of the [Display] trait).
44/// It manages the state of the markup to print, and implementations of
45/// [Display] can call into its methods to append content into the current
46/// printing session
47pub struct Formatter<'fmt> {
48    /// Stack of markup elements currently applied to the text being printed
49    state: MarkupElements<'fmt>,
50    /// Inner IO writer this [Formatter] will print text into
51    writer: &'fmt mut dyn Write,
52}
53
54impl<'fmt> Formatter<'fmt> {
55    /// Create a new instance of the [Formatter] using the provided `writer` for printing
56    pub fn new(writer: &'fmt mut dyn Write) -> Self {
57        Self {
58            state: MarkupElements::Root,
59            writer,
60        }
61    }
62
63    pub fn wrap_writer<'b: 'c, 'c>(
64        &'b mut self,
65        wrap: impl FnOnce(&'b mut dyn Write) -> &'c mut dyn Write,
66    ) -> Formatter<'c> {
67        Formatter {
68            state: self.state,
69            writer: wrap(self.writer),
70        }
71    }
72
73    /// Return a new instance of the [Formatter] with `elements` appended to its element stack
74    fn with_elements<'b>(&'b mut self, elements: &'b [MarkupElement]) -> Formatter<'b> {
75        Formatter {
76            state: MarkupElements::Node(&self.state, elements),
77            writer: self.writer,
78        }
79    }
80
81    /// Write a piece of markup into this formatter
82    pub fn write_markup(&mut self, markup: Markup) -> io::Result<()> {
83        for node in markup.0 {
84            let mut fmt = self.with_elements(node.elements);
85            node.content.fmt(&mut fmt)?;
86        }
87
88        Ok(())
89    }
90
91    /// Write a slice of text into this formatter
92    pub fn write_str(&mut self, content: &str) -> io::Result<()> {
93        self.writer.write_str(&self.state, content)
94    }
95
96    /// Write formatted text into this formatter
97    pub fn write_fmt(&mut self, content: fmt::Arguments) -> io::Result<()> {
98        self.writer.write_fmt(&self.state, content)
99    }
100}
101
102/// Formatting trait for types to be displayed as markup, the `biome_console`
103/// equivalent to [std::fmt::Display]
104///
105/// # Example
106/// Implementing `Display` on a custom struct
107/// ```
108/// use biome_console::{
109///     fmt::{Display, Formatter},
110///     markup,
111/// };
112/// use std::io;
113///
114/// struct Warning(String);
115///
116/// impl Display for Warning {
117///     fn fmt(&self, fmt: &mut Formatter) -> io::Result<()> {
118///         fmt.write_markup(markup! {
119///             <Warn>{self.0}</Warn>
120///         })
121///     }
122/// }
123///
124/// let warning = Warning(String::from("content"));
125/// markup! {
126///     <Emphasis>{warning}</Emphasis>
127/// };
128/// ```
129pub trait Display {
130    fn fmt(&self, fmt: &mut Formatter) -> io::Result<()>;
131}
132
133// Blanket implementations of Display for reference types
134impl<T> Display for &T
135where
136    T: Display + ?Sized,
137{
138    fn fmt(&self, fmt: &mut Formatter) -> io::Result<()> {
139        T::fmt(self, fmt)
140    }
141}
142
143// Blanket implementations of Display for boxed values
144impl<T> Display for Box<T>
145where
146    T: Display + ?Sized,
147{
148    fn fmt(&self, fmt: &mut Formatter) -> io::Result<()> {
149        T::fmt(&**self, fmt)
150    }
151}
152
153impl<T> Display for Cow<'_, T>
154where
155    T: Display + ToOwned + ?Sized,
156{
157    fn fmt(&self, fmt: &mut Formatter) -> io::Result<()> {
158        T::fmt(self, fmt)
159    }
160}
161
162// Simple implementations of Display calling through to write_str for types
163// that implement Deref<str>
164impl Display for str {
165    fn fmt(&self, fmt: &mut Formatter) -> io::Result<()> {
166        fmt.write_str(self)
167    }
168}
169
170impl Display for String {
171    fn fmt(&self, fmt: &mut Formatter) -> io::Result<()> {
172        fmt.write_str(self)
173    }
174}
175
176// Implement Display for Markup and Rust format Arguments
177impl Display for Markup<'_> {
178    fn fmt(&self, fmt: &mut Formatter) -> io::Result<()> {
179        fmt.write_markup(*self)
180    }
181}
182
183impl Display for std::fmt::Arguments<'_> {
184    fn fmt(&self, fmt: &mut Formatter) -> io::Result<()> {
185        fmt.write_fmt(*self)
186    }
187}
188
189/// Implement [Display] for types that implement [std::fmt::Display] by calling
190/// through to [Formatter::write_fmt]
191macro_rules! impl_std_display {
192    ($ty:ty) => {
193        impl Display for $ty {
194            fn fmt(&self, fmt: &mut Formatter) -> io::Result<()> {
195                write!(fmt, "{self}")
196            }
197        }
198    };
199}
200
201impl_std_display!(char);
202impl_std_display!(i8);
203impl_std_display!(i16);
204impl_std_display!(i32);
205impl_std_display!(i64);
206impl_std_display!(i128);
207impl_std_display!(isize);
208impl_std_display!(u8);
209impl_std_display!(u16);
210impl_std_display!(u32);
211impl_std_display!(u64);
212impl_std_display!(u128);
213impl_std_display!(usize);
214
215impl Display for Duration {
216    fn fmt(&self, fmt: &mut Formatter) -> io::Result<()> {
217        use crate as biome_console;
218
219        let secs = self.as_secs();
220        if secs > 1 {
221            return fmt.write_markup(markup! {
222                {secs}<Dim>"s"</Dim>
223            });
224        }
225
226        let millis = self.as_millis();
227        if millis > 1 {
228            return fmt.write_markup(markup! {
229                {millis}<Dim>"ms"</Dim>
230            });
231        }
232
233        let micros = self.as_micros();
234        if micros > 1 {
235            return fmt.write_markup(markup! {
236                {micros}<Dim>"µs"</Dim>
237            });
238        }
239
240        let nanos = self.as_nanos();
241        fmt.write_markup(markup! {
242            {nanos}<Dim>"ns"</Dim>
243        })
244    }
245}
246
247#[repr(transparent)]
248#[derive(Clone, Copy, Debug)]
249pub struct Bytes(pub usize);
250
251impl std::fmt::Display for Bytes {
252    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
253        let Self(mut value) = *self;
254
255        if value < 1024 {
256            return write!(fmt, "{value} B");
257        }
258
259        const PREFIX: [char; 4] = ['K', 'M', 'G', 'T'];
260        let prefix = PREFIX
261            .into_iter()
262            .find(|_| {
263                let next_value = value / 1024;
264                if next_value < 1024 {
265                    return true;
266                }
267
268                value = next_value;
269                false
270            })
271            .unwrap_or('T');
272
273        write!(fmt, "{:.1} {prefix}iB", value as f32 / 1024.0)
274    }
275}
276
277impl Display for Bytes {
278    fn fmt(&self, fmt: &mut Formatter) -> io::Result<()> {
279        write!(fmt, "{self}")
280    }
281}
282
283#[cfg(test)]
284mod tests {
285    use crate::fmt::Bytes;
286
287    #[test]
288    fn display_bytes() {
289        // Examples taken from https://stackoverflow.com/a/3758880
290        assert_eq!(Bytes(0).to_string(), "0 B");
291        assert_eq!(Bytes(27).to_string(), "27 B");
292        assert_eq!(Bytes(999).to_string(), "999 B");
293        assert_eq!(Bytes(1_000).to_string(), "1000 B");
294        assert_eq!(Bytes(1_023).to_string(), "1023 B");
295        assert_eq!(Bytes(1_024).to_string(), "1.0 KiB");
296        assert_eq!(Bytes(1_728).to_string(), "1.7 KiB");
297        assert_eq!(Bytes(110_592).to_string(), "108.0 KiB");
298        assert_eq!(Bytes(999_999).to_string(), "976.6 KiB");
299        assert_eq!(Bytes(7_077_888).to_string(), "6.8 MiB");
300        assert_eq!(Bytes(452_984_832).to_string(), "432.0 MiB");
301        assert_eq!(Bytes(28_991_029_248).to_string(), "27.0 GiB");
302        assert_eq!(Bytes(1_855_425_871_872).to_string(), "1.7 TiB");
303
304        #[cfg(target_pointer_width = "32")]
305        assert_eq!(Bytes(usize::MAX).to_string(), "4.0 GiB");
306        #[cfg(target_pointer_width = "64")]
307        assert_eq!(Bytes(usize::MAX).to_string(), "16384.0 TiB");
308    }
309}