use std::{borrow::Cow, fmt, io, time::Duration};
pub use crate::write::{Termcolor, Write, HTML};
use crate::{markup, Markup, MarkupElement};
#[derive(Clone, Copy)]
pub enum MarkupElements<'a> {
Root,
Node(&'a Self, &'a [MarkupElement<'a>]),
}
impl<'a> MarkupElements<'a> {
pub fn for_each(
&self,
func: &mut impl FnMut(&'a [MarkupElement]) -> io::Result<()>,
) -> io::Result<()> {
if let Self::Node(parent, elem) = self {
parent.for_each(func)?;
func(elem)?;
}
Ok(())
}
pub fn for_each_rev(
&self,
func: &mut impl FnMut(&'a [MarkupElement]) -> io::Result<()>,
) -> io::Result<()> {
if let Self::Node(parent, elem) = self {
func(elem)?;
parent.for_each(func)?;
}
Ok(())
}
}
pub struct Formatter<'fmt> {
state: MarkupElements<'fmt>,
writer: &'fmt mut dyn Write,
}
impl<'fmt> Formatter<'fmt> {
pub fn new(writer: &'fmt mut dyn Write) -> Self {
Self {
state: MarkupElements::Root,
writer,
}
}
pub fn wrap_writer<'b: 'c, 'c>(
&'b mut self,
wrap: impl FnOnce(&'b mut dyn Write) -> &'c mut dyn Write,
) -> Formatter<'c> {
Formatter {
state: self.state,
writer: wrap(self.writer),
}
}
fn with_elements<'b>(&'b mut self, elements: &'b [MarkupElement]) -> Formatter<'b> {
Formatter {
state: MarkupElements::Node(&self.state, elements),
writer: self.writer,
}
}
pub fn write_markup(&mut self, markup: Markup) -> io::Result<()> {
for node in markup.0 {
let mut fmt = self.with_elements(node.elements);
node.content.fmt(&mut fmt)?;
}
Ok(())
}
pub fn write_str(&mut self, content: &str) -> io::Result<()> {
self.writer.write_str(&self.state, content)
}
pub fn write_fmt(&mut self, content: fmt::Arguments) -> io::Result<()> {
self.writer.write_fmt(&self.state, content)
}
}
pub trait Display {
fn fmt(&self, fmt: &mut Formatter) -> io::Result<()>;
}
impl<T> Display for &T
where
T: Display + ?Sized,
{
fn fmt(&self, fmt: &mut Formatter) -> io::Result<()> {
T::fmt(self, fmt)
}
}
impl<T> Display for Box<T>
where
T: Display + ?Sized,
{
fn fmt(&self, fmt: &mut Formatter) -> io::Result<()> {
T::fmt(&**self, fmt)
}
}
impl<T> Display for Cow<'_, T>
where
T: Display + ToOwned + ?Sized,
{
fn fmt(&self, fmt: &mut Formatter) -> io::Result<()> {
T::fmt(self, fmt)
}
}
impl Display for str {
fn fmt(&self, fmt: &mut Formatter) -> io::Result<()> {
fmt.write_str(self)
}
}
impl Display for String {
fn fmt(&self, fmt: &mut Formatter) -> io::Result<()> {
fmt.write_str(self)
}
}
impl Display for Markup<'_> {
fn fmt(&self, fmt: &mut Formatter) -> io::Result<()> {
fmt.write_markup(*self)
}
}
impl Display for std::fmt::Arguments<'_> {
fn fmt(&self, fmt: &mut Formatter) -> io::Result<()> {
fmt.write_fmt(*self)
}
}
macro_rules! impl_std_display {
($ty:ty) => {
impl Display for $ty {
fn fmt(&self, fmt: &mut Formatter) -> io::Result<()> {
write!(fmt, "{self}")
}
}
};
}
impl_std_display!(char);
impl_std_display!(i8);
impl_std_display!(i16);
impl_std_display!(i32);
impl_std_display!(i64);
impl_std_display!(i128);
impl_std_display!(isize);
impl_std_display!(u8);
impl_std_display!(u16);
impl_std_display!(u32);
impl_std_display!(u64);
impl_std_display!(u128);
impl_std_display!(usize);
impl Display for Duration {
fn fmt(&self, fmt: &mut Formatter) -> io::Result<()> {
use crate as biome_console;
let secs = self.as_secs();
if secs > 1 {
return fmt.write_markup(markup! {
{secs}<Dim>"s"</Dim>
});
}
let millis = self.as_millis();
if millis > 1 {
return fmt.write_markup(markup! {
{millis}<Dim>"ms"</Dim>
});
}
let micros = self.as_micros();
if micros > 1 {
return fmt.write_markup(markup! {
{micros}<Dim>"µs"</Dim>
});
}
let nanos = self.as_nanos();
fmt.write_markup(markup! {
{nanos}<Dim>"ns"</Dim>
})
}
}
#[repr(transparent)]
#[derive(Clone, Copy, Debug)]
pub struct Bytes(pub usize);
impl std::fmt::Display for Bytes {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
let Self(mut value) = *self;
if value < 1024 {
return write!(fmt, "{value} B");
}
const PREFIX: [char; 4] = ['K', 'M', 'G', 'T'];
let prefix = PREFIX
.into_iter()
.find(|_| {
let next_value = value / 1024;
if next_value < 1024 {
return true;
}
value = next_value;
false
})
.unwrap_or('T');
write!(fmt, "{:.1} {prefix}iB", value as f32 / 1024.0)
}
}
impl Display for Bytes {
fn fmt(&self, fmt: &mut Formatter) -> io::Result<()> {
write!(fmt, "{self}")
}
}
#[cfg(test)]
mod tests {
use crate::fmt::Bytes;
#[test]
fn display_bytes() {
assert_eq!(Bytes(0).to_string(), "0 B");
assert_eq!(Bytes(27).to_string(), "27 B");
assert_eq!(Bytes(999).to_string(), "999 B");
assert_eq!(Bytes(1_000).to_string(), "1000 B");
assert_eq!(Bytes(1_023).to_string(), "1023 B");
assert_eq!(Bytes(1_024).to_string(), "1.0 KiB");
assert_eq!(Bytes(1_728).to_string(), "1.7 KiB");
assert_eq!(Bytes(110_592).to_string(), "108.0 KiB");
assert_eq!(Bytes(999_999).to_string(), "976.6 KiB");
assert_eq!(Bytes(7_077_888).to_string(), "6.8 MiB");
assert_eq!(Bytes(452_984_832).to_string(), "432.0 MiB");
assert_eq!(Bytes(28_991_029_248).to_string(), "27.0 GiB");
assert_eq!(Bytes(1_855_425_871_872).to_string(), "1.7 TiB");
#[cfg(target_pointer_width = "32")]
assert_eq!(Bytes(usize::MAX).to_string(), "4.0 GiB");
#[cfg(target_pointer_width = "64")]
assert_eq!(Bytes(usize::MAX).to_string(), "16384.0 TiB");
}
}