use core::cmp::min;
use core::ops::{Bound, RangeBounds, RangeFull};
use crate::{
grid::records::{ExactRecords, Records, Resizable},
settings::TableOption,
};
#[cfg_attr(feature = "std", doc = "```")]
#[cfg_attr(not(feature = "std"), doc = "```ignore")]
#[derive(Debug)]
pub struct Extract<R, C> {
rows: R,
columns: C,
}
impl<R, C> Extract<R, C>
where
R: RangeBounds<usize>,
C: RangeBounds<usize>,
{
pub fn segment(rows: R, columns: C) -> Self {
Extract { rows, columns }
}
}
impl<R> Extract<R, RangeFull>
where
R: RangeBounds<usize>,
{
pub fn rows(rows: R) -> Self {
Extract { rows, columns: .. }
}
}
impl<C> Extract<RangeFull, C>
where
C: RangeBounds<usize>,
{
pub fn columns(columns: C) -> Self {
Extract { rows: .., columns }
}
}
impl<R, C, RR, D, Cfg> TableOption<RR, Cfg, D> for Extract<R, C>
where
R: RangeBounds<usize> + Clone,
C: RangeBounds<usize> + Clone,
RR: Records + ExactRecords + Resizable,
{
fn change(self, records: &mut RR, _: &mut Cfg, _: &mut D) {
let count_rows = records.count_rows();
let count_columns = records.count_columns();
let mut rows = bounds_to_usize(self.rows.start_bound(), self.rows.end_bound(), count_rows);
let mut cols = bounds_to_usize(
self.columns.start_bound(),
self.columns.end_bound(),
count_columns,
);
rows.0 = min(rows.0, count_rows);
cols.0 = min(cols.0, count_columns);
extract(records, (count_rows, count_columns), rows, cols);
}
}
fn extract<R>(
records: &mut R,
(count_rows, count_cols): (usize, usize),
(start_row, end_row): (usize, usize),
(start_col, end_col): (usize, usize),
) where
R: Resizable,
{
for (i, row) in (0..start_row).enumerate() {
let row = row - i;
records.remove_row(row);
}
let count_rows = count_rows - start_row;
let end_row = end_row - start_row;
for (i, row) in (end_row..count_rows).enumerate() {
let row = row - i;
records.remove_row(row);
}
for (i, col) in (0..start_col).enumerate() {
let col = col - i;
records.remove_column(col);
}
let count_cols = count_cols - start_col;
let end_col = end_col - start_col;
for (i, col) in (end_col..count_cols).enumerate() {
let col = col - i;
records.remove_column(col);
}
}
fn bounds_to_usize(
left: Bound<&usize>,
right: Bound<&usize>,
count_elements: usize,
) -> (usize, usize) {
match (left, right) {
(Bound::Included(x), Bound::Included(y)) => (*x, y + 1),
(Bound::Included(x), Bound::Excluded(y)) => (*x, *y),
(Bound::Included(x), Bound::Unbounded) => (*x, count_elements),
(Bound::Unbounded, Bound::Unbounded) => (0, count_elements),
(Bound::Unbounded, Bound::Included(y)) => (0, y + 1),
(Bound::Unbounded, Bound::Excluded(y)) => (0, *y),
(Bound::Excluded(_), Bound::Unbounded)
| (Bound::Excluded(_), Bound::Included(_))
| (Bound::Excluded(_), Bound::Excluded(_)) => {
unreachable!("A start bound can't be excluded")
}
}
}