m2 0.0.0

Set of Unix tools to work with m2dirs
Documentation
use std::fmt::Display;

use crate::{format::Format, utils::terminal_size};

#[derive(Debug)]
pub struct Cell {
    content: String,
    format: Format,
}

impl Cell {
    pub fn new(content: impl Into<String>) -> Self {
        Self {
            content: content.into(),
            format: Format::None,
        }
    }

    pub fn format(mut self, format: Format) -> Self {
        self.format = format;
        self
    }
}

impl Display for Cell {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "{}", self.format)?;
        f.pad(&self.content)?;
        write!(f, "{}", Format::Reset)
    }
}

#[derive(Debug)]
pub struct Table {
    padding: usize,
    cells: Vec<Vec<Cell>>,
    center: bool,
}

impl Default for Table {
    fn default() -> Self {
        Self {
            padding: 2,
            cells: Vec::new(),
            center: false,
        }
    }
}

impl Table {
    pub fn padding(mut self, padding: usize) -> Self {
        self.padding = padding;
        self
    }

    pub fn center(mut self) -> Self {
        self.center = true;
        self
    }

    pub fn row(&mut self, row: Vec<Cell>) {
        self.cells.push(row);
    }

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

    pub fn is_empty(&self) -> bool {
        self.cells.is_empty()
    }
}

impl Display for Table {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        let n_cols = match self.cells.first() {
            Some(row) => row.len(),
            None => return Ok(()),
        };

        let widths: Vec<usize> = (0..n_cols)
            .map(|i| {
                self.cells
                    .iter()
                    .filter_map(|row| row.get(i))
                    .map(|cell| cell.content.chars().count())
                    .max()
                    .unwrap_or(0)
                    + self.padding
            })
            .collect();

        let cols = terminal_size().map(|(cols, _)| cols).unwrap_or(u16::MAX);

        for row in self.cells.iter() {
            let mut cols_left = cols as usize;
            for (cell, width) in row.iter().zip(widths.iter()) {
                match cols_left.checked_sub(cell.content.len()) {
                    Some(_) => match cols_left.checked_sub(*width) {
                        Some(new) => {
                            cols_left = new;
                            if self.center {
                                write!(f, "{:^width$}", cell)?;
                            } else {
                                write!(f, "{:<width$}", cell)?;
                            }
                        }
                        None => write!(f, "{cell}")?,
                    },
                    None => {
                        cell.content
                            .chars()
                            .take(cols_left - 1)
                            .try_for_each(|c| write!(f, "{c}"))?;
                        write!(f, "")?;
                        break;
                    }
                }
            }
            writeln!(f)?;
        }

        Ok(())
    }
}