tv-cli 0.7.0

Format json into table view
Documentation
use unicode_width::UnicodeWidthStr;

#[derive(Debug, Clone, Copy)]
pub enum Align {
    None,
    Left,
    Center,
    Right,
}

impl Align {
    pub fn new(s: String) -> Self {
        match s.to_lowercase().as_str() {
            "l" | "left" => Self::Left,
            "c" | "center" => Self::Center,
            "r" | "right" => Self::Right,
            _ => Self::None,
        }
    }
}

#[derive(Debug, Clone)]
pub struct Cell<T>
where
    T: std::fmt::Display,
{
    value: T,
    width: usize,
    align: Align,
}

impl<T> Cell<T>
where
    T: std::fmt::Display + Clone,
{
    pub fn new(v: T) -> Self {
        Self {
            value: v.clone(),
            width: format!("{}", v.clone()).width(),
            align: Align::None,
        }
    }

    pub fn set_width(&mut self, w: usize) -> &mut Self {
        self.width = w;
        self
    }

    pub fn set_align(&mut self, a: Align) -> &mut Self {
        self.align = a;
        self
    }

    pub fn width(&self) -> usize {
        self.width
    }
}

impl<T> std::fmt::Display for Cell<T>
where
    T: std::fmt::Display,
{
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        let s = match self.align {
            Align::Right => {
                format!(
                    "{}{}",
                    " ".repeat(self.width - format!("{}", self.value).width()),
                    self.value
                )
            }
            Align::None | Align::Left => format!(
                "{}{}",
                self.value,
                " ".repeat(self.width - format!("{}", self.value).width())
            ),
            Align::Center => {
                let pad = (self.width - format!("{}", self.value).width()) as f32 / 2.0;
                let left_pad = pad.ceil() as usize;
                let right_pad = pad.floor() as usize;
                format!(
                    "{}{}{}",
                    " ".repeat(left_pad),
                    self.value,
                    " ".repeat(right_pad)
                )
            }
        };

        write!(f, "{}", s)
    }
}

#[cfg(test)]
mod tests {
    use super::{Align, Cell};

    #[test]
    fn create_cell() {
        let cell = Cell::new(String::from("Hello"));
        assert_eq!(cell.value, String::from("Hello"));
    }

    #[test]
    fn display_cell() {
        let cell = Cell::new(String::from("Hello"));
        assert_eq!("Hello", format!("{}", cell))
    }

    #[test]
    fn display_cell_with_width() {
        let mut cell = Cell::new(String::from("Hello"));
        cell.set_width(10);
        assert_eq!("Hello     ", format!("{}", cell))
    }

    #[test]
    fn display_cell_with_align() {
        struct TestCase<'a> {
            value: &'a str,
            width: usize,
            align: Align,
            expect: &'a str,
        }

        let data_set = vec![
            TestCase {
                value: "Hello",
                width: 10,
                align: Align::None,
                expect: "Hello     ",
            },
            TestCase {
                value: "Hello",
                width: 10,
                align: Align::Left,
                expect: "Hello     ",
            },
            TestCase {
                value: "Hello",
                width: 10,
                align: Align::Center,
                expect: "   Hello  ",
            },
            TestCase {
                value: "Hello",
                width: 10,
                align: Align::Right,
                expect: "     Hello",
            },
        ];

        data_set.iter().for_each(|case| {
            let mut cell = Cell::new(case.value);
            cell.set_width(case.width).set_align(case.align);
            assert_eq!(case.expect, format!("{}", cell));
        });
    }
}