tabled 0.20.0

An easy to use library for pretty print tables of Rust `struct`s and `enum`s.
Documentation
//! This module contains an [`Dup`] setting the [`Table`].
//!
//! # Example
//!
//! ```
//! # use tabled::{Table, settings::{Dup, object::{Columns, Rows}}};
//! # let data: Vec<&'static str> = Vec::new();
//! let mut table = Table::new(&data);
//! table.with(Dup::new(Rows::first(), Columns::first()));
//! ```
//!
//! [`Table`]: crate::Table

use crate::{
    grid::config::Position,
    grid::records::{ExactRecords, PeekableRecords, Records, RecordsMut},
    settings::{object::Object, TableOption},
};

/// [`Dup`] duplicates a given set of cells into another set of ones [`Table`].
///
/// # Example
///
/// ```
/// use tabled::{Table, settings::{object::Rows, Dup}};
///
/// let data = [
///     ["1", "2", "3"],
///     ["Some\nMulti\nLine\nText", "and a line", "here"],
///     ["4", "5", "6"],
/// ];
///
/// let mut table = Table::new(&data);
/// table.with(Dup::new(Rows::one(1), Rows::one(2)));
///
/// assert_eq!(
///     table.to_string(),
///     "+-------+------------+------+\n\
///      | 0     | 1          | 2    |\n\
///      +-------+------------+------+\n\
///      | Some  | and a line | here |\n\
///      | Multi |            |      |\n\
///      | Line  |            |      |\n\
///      | Text  |            |      |\n\
///      +-------+------------+------+\n\
///      | Some  | and a line | here |\n\
///      | Multi |            |      |\n\
///      | Line  |            |      |\n\
///      | Text  |            |      |\n\
///      +-------+------------+------+\n\
///      | 4     | 5          | 6    |\n\
///      +-------+------------+------+",
/// )
/// ```
///
/// [`Table`]: crate::Table
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)]
pub struct Dup<Dst, Src> {
    src: Src,
    dst: Dst,
}

impl<Dst, Src> Dup<Dst, Src> {
    /// New creates a new [`Dup`] modifier.
    ///
    /// # Example
    ///
    /// ```
    /// # use tabled::{Table, settings::{Dup, object::{Columns, Rows}}};
    /// # let data: Vec<&'static str> = Vec::new();
    /// let mut table = Table::new(&data);
    /// table.with(Dup::new(Rows::first(), Columns::last()));
    /// ```
    pub fn new(dst: Dst, src: Src) -> Self {
        Self { src, dst }
    }
}

impl<Dst, Src, R, D, C> TableOption<R, C, D> for Dup<Dst, Src>
where
    Dst: Object<R>,
    Src: Object<R>,
    R: Records + ExactRecords + PeekableRecords + RecordsMut<String>,
{
    fn change(self, records: &mut R, _: &mut C, _: &mut D) {
        let input = collect_input(records, self.src);
        set_cells(records, &input, self.dst);
    }
}

fn collect_input<R, O>(records: &mut R, src: O) -> Vec<String>
where
    O: Object<R>,
    R: Records + ExactRecords + PeekableRecords + RecordsMut<String>,
{
    let count_rows = records.count_rows();
    let count_columns = records.count_columns();

    let mut input = Vec::new();
    for entity in src.cells(records) {
        for pos in entity.iter(count_rows, count_columns) {
            if !is_valid_cell(pos, count_rows, count_columns) {
                continue;
            }

            let text = records.get_text(pos).to_owned();
            input.push(text);
        }
    }

    input
}

fn set_cells<R, O>(records: &mut R, src: &[String], dst: O)
where
    O: Object<R>,
    R: Records + ExactRecords + PeekableRecords + RecordsMut<String>,
{
    if src.is_empty() {
        return;
    }

    let count_rows = records.count_rows();
    let count_columns = records.count_columns();

    for entity in dst.cells(records) {
        let mut source = src.iter().cycle();
        for pos in entity.iter(count_rows, count_columns) {
            if !is_valid_cell(pos, count_rows, count_columns) {
                continue;
            }

            let text = source.next().unwrap().clone();
            records.set(pos, text);
        }
    }
}

fn is_valid_cell(pos: Position, count_rows: usize, count_columns: usize) -> bool {
    if pos.row > count_rows {
        return false;
    }

    if pos.col > count_columns {
        return false;
    }

    true
}