timmy 0.2.0

A time tracker mainly for programming tasks
Documentation
use std::iter;

#[derive(Debug, Clone)]
pub enum CellType {
    Separator,
    Data(String),
}

#[derive(Debug, Clone)]
pub struct Cell {
    typ: CellType,
    border_left: String,
    border_right: String,
}

impl Cell {
    pub fn new_left_bordered(t: CellType, border: &str) -> Cell {
        Cell {typ: t, border_left: border.into(), border_right: "".into()}
    }

    pub fn new_both_bordered(t: CellType, left: &str, right: &str) -> Cell {
        Cell {typ: t, border_left: left.into(), border_right: right.into()}
    }

    pub fn new_right_bordered(t: CellType, border: &str) -> Cell {
        Cell {typ: t, border_left: "".into(), border_right: border.into()}
    }

    fn print(&self, width: usize) {
        let middle = match self.typ {
            CellType::Separator => iter::repeat("").take(width+2).collect(),
            CellType::Data(ref s) => {
                let to_pad = width - s.len();
                let spaces: String = iter::repeat(" ").take(to_pad).collect();
                format!(" {}{} ", s, spaces)
            },
        };
        print!("{}{}{}", self.border_left, middle, self.border_right);
    }

    fn len(&self) -> usize {
        match self.typ {
            CellType::Separator => 0,
            CellType::Data(ref s) => s.len(),
        }
    }
}

pub struct Table {
    rows: Vec<Vec<Cell>>,
    cols: usize,
}

impl Table {
    pub fn with_headers(headers: Vec<String>) -> Table {
        let mut table = Table {rows: vec![], cols: headers.len()};
        table.add_border_top();
        table.add_simple(headers);
        table.add_full_separator();
        table
    }

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

    pub fn add_simple(&mut self, data: Vec<String>) {
        let len = data.len();
        let cells =
            data.into_iter()
                .enumerate()
                .map(|(i, data)| {
                    if i != len-1 {
                        Cell::new_left_bordered(CellType::Data(data), "")
                    } else {
                        Cell::new_both_bordered(CellType::Data(data), "", "")
                    }
                })
                .collect();
        self.rows.push(cells);
    }

    pub fn add_full_separator(&mut self) {
        self.add_full_separator_custom("", "", "");
    }

    pub fn add_border_top(&mut self) {
        self.add_full_separator_custom("", "", "");
    }

    pub fn add_border_bottom(&mut self) {
        self.add_full_separator_custom("", "", "");
    }

    pub fn add_full_separator_custom(&mut self, left: &str, middle: &str, right: &str) {
        let mut cells = vec![];
        let middle_cell = Cell::new_left_bordered(CellType::Separator, middle);
        if self.cols == 1 {
            cells.push(Cell::new_both_bordered(CellType::Separator, left, right));
        } else {
            cells.push(Cell::new_left_bordered(CellType::Separator, left));
        }
        let mut middle_cells: Vec<Cell> = (0..self.cols-2).map(|_| middle_cell.clone()).collect();
        cells.append(&mut middle_cells);
        if self.cols != 1 {
            cells.push(Cell::new_both_bordered(CellType::Separator, middle, right));
        }
        self.rows.push(cells);
    }

    pub fn print(&self) {
        let max_lengths: Vec<usize> = (0..self.cols)
            .map(|i| {
                let lens = self.rows.iter().map(|row| row[i].len());
                lens.max().unwrap()
            })
            .collect();

        for row in &self.rows {
            for (i, cell) in row.iter().enumerate() {
                cell.print(max_lengths[i]);
            }
            println!("");
        }
    }
}