tabled/settings/duplicate/
mod.rs

1//! This module contains an [`Dup`] setting the [`Table`].
2//!
3//! # Example
4//!
5//! ```
6//! # use tabled::{Table, settings::{Dup, object::{Columns, Rows}}};
7//! # let data: Vec<&'static str> = Vec::new();
8//! let mut table = Table::new(&data);
9//! table.with(Dup::new(Rows::first(), Columns::first()));
10//! ```
11//!
12//! [`Table`]: crate::Table
13
14use crate::{
15    grid::config::Position,
16    grid::records::{ExactRecords, PeekableRecords, Records, RecordsMut},
17    settings::{object::Object, TableOption},
18};
19
20/// [`Dup`] duplicates a given set of cells into another set of ones [`Table`].
21///
22/// # Example
23///
24/// ```
25/// use tabled::{Table, settings::{object::Rows, Dup}};
26///
27/// let data = [
28///     ["1", "2", "3"],
29///     ["Some\nMulti\nLine\nText", "and a line", "here"],
30///     ["4", "5", "6"],
31/// ];
32///
33/// let mut table = Table::new(&data);
34/// table.with(Dup::new(Rows::one(1), Rows::one(2)));
35///
36/// assert_eq!(
37///     table.to_string(),
38///     "+-------+------------+------+\n\
39///      | 0     | 1          | 2    |\n\
40///      +-------+------------+------+\n\
41///      | Some  | and a line | here |\n\
42///      | Multi |            |      |\n\
43///      | Line  |            |      |\n\
44///      | Text  |            |      |\n\
45///      +-------+------------+------+\n\
46///      | Some  | and a line | here |\n\
47///      | Multi |            |      |\n\
48///      | Line  |            |      |\n\
49///      | Text  |            |      |\n\
50///      +-------+------------+------+\n\
51///      | 4     | 5          | 6    |\n\
52///      +-------+------------+------+",
53/// )
54/// ```
55///
56/// [`Table`]: crate::Table
57#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)]
58pub struct Dup<Dst, Src> {
59    src: Src,
60    dst: Dst,
61}
62
63impl<Dst, Src> Dup<Dst, Src> {
64    /// New creates a new [`Dup`] modifier.
65    ///
66    /// # Example
67    ///
68    /// ```
69    /// # use tabled::{Table, settings::{Dup, object::{Columns, Rows}}};
70    /// # let data: Vec<&'static str> = Vec::new();
71    /// let mut table = Table::new(&data);
72    /// table.with(Dup::new(Rows::first(), Columns::last()));
73    /// ```
74    pub fn new(dst: Dst, src: Src) -> Self {
75        Self { src, dst }
76    }
77}
78
79impl<Dst, Src, R, D, C> TableOption<R, C, D> for Dup<Dst, Src>
80where
81    Dst: Object<R>,
82    Src: Object<R>,
83    R: Records + ExactRecords + PeekableRecords + RecordsMut<String>,
84{
85    fn change(self, records: &mut R, _: &mut C, _: &mut D) {
86        let input = collect_input(records, self.src);
87        set_cells(records, &input, self.dst);
88    }
89}
90
91fn collect_input<R, O>(records: &mut R, src: O) -> Vec<String>
92where
93    O: Object<R>,
94    R: Records + ExactRecords + PeekableRecords + RecordsMut<String>,
95{
96    let count_rows = records.count_rows();
97    let count_columns = records.count_columns();
98
99    let mut input = Vec::new();
100    for entity in src.cells(records) {
101        for pos in entity.iter(count_rows, count_columns) {
102            if !is_valid_cell(pos, count_rows, count_columns) {
103                continue;
104            }
105
106            let text = records.get_text(pos).to_owned();
107            input.push(text);
108        }
109    }
110
111    input
112}
113
114fn set_cells<R, O>(records: &mut R, src: &[String], dst: O)
115where
116    O: Object<R>,
117    R: Records + ExactRecords + PeekableRecords + RecordsMut<String>,
118{
119    if src.is_empty() {
120        return;
121    }
122
123    let count_rows = records.count_rows();
124    let count_columns = records.count_columns();
125
126    for entity in dst.cells(records) {
127        let mut source = src.iter().cycle();
128        for pos in entity.iter(count_rows, count_columns) {
129            if !is_valid_cell(pos, count_rows, count_columns) {
130                continue;
131            }
132
133            let text = source.next().unwrap().clone();
134            records.set(pos, text);
135        }
136    }
137}
138
139fn is_valid_cell(pos: Position, count_rows: usize, count_columns: usize) -> bool {
140    if pos.row > count_rows {
141        return false;
142    }
143
144    if pos.col > count_columns {
145        return false;
146    }
147
148    true
149}