tabled/features/
disable.rs

1//! This module contains a [`Disable`] structure which helps to
2//! remove an etheir column or row from a [`Table`].
3//!
4//! Generally you should avoid use of [`Disable`] because it's a slow function and modifies the underlying records.
5//! Providing correct data right away is better.
6//!
7//! # Example
8//!
9//! ```
10//! use tabled::{Disable, TableIteratorExt, object::Rows};
11//!
12//! let data = vec!["Hello", "World", "!!!"];
13//!
14//! let table = data.table().with(Disable::row(Rows::new(1..2))).to_string();
15//!
16//! assert_eq!(
17//!     table,
18//!     "+-------+\n\
19//!      | &str  |\n\
20//!      +-------+\n\
21//!      | World |\n\
22//!      +-------+\n\
23//!      | !!!   |\n\
24//!      +-------+"
25//! );
26//! ```
27//!
28//! [`Table`]: crate::Table
29
30use std::marker::PhantomData;
31
32use papergrid::records::{Records, Resizable};
33
34use crate::{locator::Locator, Table, TableOption};
35
36/// Disable removes particular rows/columns from a [`Table`].
37///
38/// It tries to keeps track of style changes which may occur.
39/// But it's not guaranteed will be the way you would expect it to be.
40///
41/// # Example
42///
43/// ```rust,no_run
44/// # use tabled::{Disable, Table, object::Rows};
45/// # let data: Vec<&'static str> = Vec::new();
46/// let table = Table::new(&data).with(Disable::row(Rows::first()));
47/// ```
48///
49/// [`Table`]: crate::Table
50#[derive(Debug)]
51pub struct Disable<L, Target> {
52    locator: L,
53    target: PhantomData<Target>,
54}
55
56impl<L> Disable<L, TargetColumn> {
57    /// Disable columns.
58    ///
59    /// Available locators are:
60    ///
61    /// - [`Columns`]
62    /// - [`Column`]
63    /// - [`FirstColumn`]
64    /// - [`LastColumn`]
65    /// - [`ByColumnName`]
66    ///
67    /// ```rust
68    /// use tabled::{Disable, locator::ByColumnName, builder::Builder, object::Columns};
69    ///
70    /// let mut builder = Builder::default();
71    ///
72    /// builder.add_record(["col1", "col2", "col3"]);
73    /// builder.add_record(["Hello", "World", "1"]);
74    ///
75    /// let table = builder.build()
76    ///     .with(Disable::column(ByColumnName::new("col3")))
77    ///     .to_string();
78    ///
79    /// assert_eq!(
80    ///     table,
81    ///     "+-------+-------+\n\
82    ///      | col1  | col2  |\n\
83    ///      +-------+-------+\n\
84    ///      | Hello | World |\n\
85    ///      +-------+-------+"
86    /// );
87    /// ```
88    ///
89    /// [`Columns`]: crate::object::Columns
90    /// [`Column`]: crate::object::Column
91    /// [`FirstColumn`]: crate::object::FirstColumn
92    /// [`LastColumn`]: crate::object::LastColumn
93    /// [`ByColumnName`]: crate::locator::ByColumnName
94    pub fn column(locator: L) -> Self
95    where
96        L: Locator<Coordinate = usize>,
97    {
98        Self {
99            locator,
100            target: PhantomData,
101        }
102    }
103}
104
105impl<L> Disable<L, TargetRow> {
106    /// Disable rows.
107    ///
108    /// Available locators are:
109    ///
110    /// - [`Rows`]
111    /// - [`Row`]
112    /// - [`FirstRow`]
113    /// - [`LastRow`]
114    ///
115    /// ```rust
116    /// use tabled::{Disable, builder::Builder, object::Rows};
117    ///
118    /// let mut builder = Builder::default();
119    ///
120    /// builder.add_record(["col1", "col2", "col3"]);
121    /// builder.add_record(["Hello", "World", "1"]);
122    ///
123    /// let table = builder.build()
124    ///     .with(Disable::row(Rows::first()))
125    ///     .to_string();
126    ///
127    /// assert_eq!(
128    ///     table,
129    ///     "+-------+-------+---+\n\
130    ///      | Hello | World | 1 |\n\
131    ///      +-------+-------+---+"
132    /// );
133    /// ```
134    ///
135    /// [`Rows`]: crate::object::Rows
136    /// [`Row`]: crate::object::Row
137    /// [`FirstRow`]: crate::object::FirstRow
138    /// [`LastRow`]: crate::object::LastRow
139    pub fn row(locator: L) -> Self
140    where
141        L: Locator<Coordinate = usize>,
142    {
143        Self {
144            locator,
145            target: PhantomData,
146        }
147    }
148}
149
150/// A marker struct for [`Disable`].
151#[derive(Debug)]
152pub struct TargetRow;
153
154/// A marker struct for [`Disable`].
155#[derive(Debug)]
156pub struct TargetColumn;
157
158impl<L, D> TableOption<D> for Disable<L, TargetColumn>
159where
160    L: Locator<Coordinate = usize>,
161    D: Records + Resizable,
162{
163    fn change(&mut self, table: &mut Table<D>) {
164        let columns = self.locator.locate(table.get_records());
165        let records = table.get_records_mut();
166        let mut shift = 0;
167        for col in columns.into_iter() {
168            if col - shift > records.count_columns() {
169                continue;
170            }
171
172            records.remove_column(col - shift);
173            shift += 1;
174        }
175
176        table.destroy_width_cache();
177        table.destroy_height_cache();
178
179        // fixme: I am pretty sure that we violate span constrains by removing rows/cols
180        //        Because span may be bigger then the max number of rows/cols
181    }
182}
183
184impl<L, D> TableOption<D> for Disable<L, TargetRow>
185where
186    L: Locator<Coordinate = usize>,
187    D: Records + Resizable,
188{
189    fn change(&mut self, table: &mut Table<D>) {
190        let rows = self.locator.locate(table.get_records());
191        let records = table.get_records_mut();
192        let mut shift = 0;
193        for row in rows.into_iter() {
194            if row - shift > records.count_rows() {
195                continue;
196            }
197
198            records.remove_row(row - shift);
199            shift += 1;
200        }
201
202        table.destroy_width_cache();
203        table.destroy_height_cache();
204
205        // fixme: I am pretty sure that we violate span constrains by removing rows/cols
206        //        Because span may be bigger then the max number of rows/cols
207    }
208}