tabled/features/extract.rs
1//! This module contains an [`Extract`] structure which is used to
2//! obtain an ordinary segment from the [`Table`].
3//!
4//! There's a similar structure [`Highlight`] which does a highlighting a of segments.
5//!
6//! [`Table`]: crate::Table
7//! [`Highlight`]: crate::Highlight
8
9use std::ops::{RangeBounds, RangeFull};
10
11use papergrid::records::{Records, Resizable};
12
13use crate::{object::bounds_to_usize, Table, TableOption};
14
15/// Returns a new [`Table`] that reflects a segment of the referenced [`Table`]
16///
17/// # Example
18///
19/// ```
20/// use tabled::{Table, format::Format, object::Rows, Modify, Extract};
21///
22/// let data = vec![
23/// (0, "Grodno", true),
24/// (1, "Minsk", true),
25/// (2, "Hamburg", false),
26/// (3, "Brest", true),
27/// ];
28///
29/// let table = Table::new(&data)
30/// .with(Modify::new(Rows::new(1..)).with(Format::new(|s| format!(": {} :", s))))
31/// .with(Extract::segment(1..=2, 1..))
32/// .to_string();
33///
34/// assert_eq!(table, "+------------+----------+\n\
35/// | : Grodno : | : true : |\n\
36/// +------------+----------+\n\
37/// | : Minsk : | : true : |\n\
38/// +------------+----------+");
39/// ```
40///
41/// [`Table`]: crate::Table
42#[derive(Debug)]
43pub struct Extract<R, C> {
44 rows: R,
45 columns: C,
46}
47
48impl<R, C> Extract<R, C>
49where
50 R: RangeBounds<usize>,
51 C: RangeBounds<usize>,
52{
53 /// Returns a new [`Table`] that reflects a segment of the referenced [`Table`]
54 ///
55 /// ```rust,no_run
56 /// # use tabled::Extract;
57 /// let rows = 1..3;
58 /// let columns = 1..;
59 /// Extract::segment(rows, columns);
60 /// ```
61 ///
62 /// # Range
63 ///
64 /// A [`RangeBounds`] argument can be less than or equal to the shape of a [`Table`]
65 ///
66 /// If a [`RangeBounds`] argument is malformed or too large the thread will panic
67 ///
68 /// ```text
69 /// // Empty Full Out of bounds
70 /// Extract::segment(0..0, 0..0) Extract::segment(.., ..) Extract::segment(0..1, ..4)
71 /// []. . . [O O O [O O O X] //ERROR
72 /// . . . O O O . . .
73 /// . . . O O O] . . .
74 /// ```
75 ///
76 /// [`Table`]: crate::Table
77 pub fn segment(rows: R, columns: C) -> Self {
78 Extract { rows, columns }
79 }
80}
81
82impl<R> Extract<R, RangeFull>
83where
84 R: RangeBounds<usize>,
85{
86 /// Returns a new [`Table`] that reflects a segment of the referenced [`Table`]
87 ///
88 /// The segment is defined by [`RangeBounds`<usize>] for Rows
89 ///
90 /// ```rust,no_run
91 /// # use tabled::Extract;
92 /// Extract::rows(1..3);
93 /// ```
94 ///
95 /// # Range
96 ///
97 /// A [`RangeBounds`] argument can be less than or equal to the shape of a [`Table`]
98 ///
99 /// If a [`RangeBounds`] argument is malformed or too large the thread will panic
100 ///
101 /// ```text
102 /// // Empty Full Out of bounds
103 /// Extract::rows(0..0) Extract::rows(..) Extract::rows(0..4)
104 /// []. . . [O O O [O O O
105 /// . . . O O O O O O
106 /// . . . O O O] O O O
107 /// X X X] // ERROR
108 /// ```
109 ///
110 /// [`Table`]: crate::Table
111 pub fn rows(rows: R) -> Self {
112 Extract { rows, columns: .. }
113 }
114}
115
116impl<C> Extract<RangeFull, C>
117where
118 C: RangeBounds<usize>,
119{
120 /// Returns a new [`Table`] that reflects a segment of the referenced [`Table`]
121 ///
122 /// The segment is defined by [`RangeBounds`<usize>] for Columns
123 ///
124 /// ```rust,no_run
125 /// # use tabled::Extract;
126 /// Extract::columns(1..3);
127 /// ```
128 ///
129 /// # Range
130 ///
131 /// A [`RangeBounds`] argument can be less than or equal to the shape of a [`Table`]
132 ///
133 /// If a [`RangeBounds`] argument is malformed or too large the thread will panic
134 ///
135 /// ```text
136 /// // Empty Full Out of bounds
137 /// Extract::columns(0..0) Extract::columns(..) Extract::columns(0..4)
138 /// []. . . [O O O [O O O X
139 /// . . . O O O O O O X
140 /// . . . O O O] O O O X] // ERROR
141 /// ```
142 ///
143 /// [`Table`]: crate::Table
144 pub fn columns(columns: C) -> Self {
145 Extract { rows: .., columns }
146 }
147}
148
149impl<R, C, RR> TableOption<RR> for Extract<R, C>
150where
151 R: RangeBounds<usize> + Clone,
152 C: RangeBounds<usize> + Clone,
153 RR: Records + Resizable,
154{
155 fn change(&mut self, table: &mut Table<RR>) {
156 let shape = table.shape();
157 let mut rows = bounds_to_usize(self.rows.start_bound(), self.rows.end_bound(), shape.0);
158 let mut cols = bounds_to_usize(
159 self.columns.start_bound(),
160 self.columns.end_bound(),
161 shape.1,
162 );
163
164 // Cleanup table in case if boundries are exeeded.
165 //
166 // todo: can be optimized by adding a clear() method to Resizable
167 rows.0 = std::cmp::min(rows.0, shape.0);
168 cols.0 = std::cmp::min(cols.0, shape.1);
169
170 extract(table.get_records_mut(), shape, rows, cols);
171 }
172}
173
174/// Returns a new [`Grid`] that reflects a segment of the referenced [`Grid`].
175///
176/// # Example
177///
178/// ```text
179/// grid
180/// +---+---+---+
181/// |0-0|0-1|0-2|
182/// +---+---+---+
183/// |1-0|1-1|1-2|
184/// +---+---+---+
185/// |2-0|2-1|2-2|
186/// +---+---+---+
187///
188/// let rows = ..;
189/// let columns = ..1;
190/// grid.extract(rows, columns)
191///
192/// grid
193/// +---+
194/// |0-0|
195/// +---+
196/// |1-0|
197/// +---+
198/// |2-0|
199/// +---+
200/// ```
201fn extract<R>(
202 records: &mut R,
203 (count_rows, count_cols): (usize, usize),
204 (start_row, end_row): (usize, usize),
205 (start_col, end_col): (usize, usize),
206) where
207 R: Resizable,
208{
209 for (i, row) in (0..start_row).enumerate() {
210 let row = row - i;
211 records.remove_row(row);
212 }
213
214 let count_rows = count_rows - start_row;
215 let end_row = end_row - start_row;
216 for (i, row) in (end_row..count_rows).enumerate() {
217 let row = row - i;
218 records.remove_row(row);
219 }
220
221 for (i, col) in (0..start_col).enumerate() {
222 let col = col - i;
223 records.remove_column(col);
224 }
225
226 let count_cols = count_cols - start_col;
227 let end_col = end_col - start_col;
228 for (i, col) in (end_col..count_cols).enumerate() {
229 let col = col - i;
230 records.remove_column(col);
231 }
232}