tabled/features/
locator.rs

1//! The module contains a [`Locator`] trait and implementations for it.
2
3use std::{
4    iter::Once,
5    ops::{Range, RangeBounds},
6};
7
8use papergrid::{records::Records, Entity};
9
10use crate::{
11    object::{
12        bounds_to_usize, Column, Columns, FirstColumn, FirstRow, LastColumn, LastRow, Object, Row,
13        Rows,
14    },
15    Table,
16};
17
18/// Locator is an interface which searches for a particular thing in the [`Records`],
19/// and returns coordinate of the foundings if any.
20pub trait Locator {
21    /// A coordinate of the finding.
22    type Coordinate;
23    /// An iterator of the coordinates.
24    /// If it's empty it's consideret that nothing is found.
25    type IntoIter: IntoIterator<Item = Self::Coordinate>;
26
27    /// Search for the thing in [`Records`], returning a list of coordinates.
28    fn locate<R>(&mut self, records: R) -> Self::IntoIter
29    where
30        R: Records;
31}
32
33impl<B> Locator for Columns<B>
34where
35    B: RangeBounds<usize>,
36{
37    type Coordinate = usize;
38    type IntoIter = Range<usize>;
39
40    fn locate<R>(&mut self, records: R) -> Self::IntoIter
41    where
42        R: Records,
43    {
44        let (from, to) = bounds_to_usize(
45            self.get_range().start_bound(),
46            self.get_range().end_bound(),
47            records.count_columns(),
48        );
49
50        from..to
51    }
52}
53
54impl Locator for Column {
55    type Coordinate = usize;
56    type IntoIter = Once<usize>;
57
58    fn locate<R>(&mut self, _: R) -> Self::IntoIter
59    where
60        R: Records,
61    {
62        std::iter::once((*self).into())
63    }
64}
65
66impl Locator for FirstColumn {
67    type Coordinate = usize;
68    type IntoIter = Once<usize>;
69
70    fn locate<R>(&mut self, _: R) -> Self::IntoIter
71    where
72        R: Records,
73    {
74        std::iter::once(0)
75    }
76}
77
78impl Locator for LastColumn {
79    type Coordinate = usize;
80    type IntoIter = Once<usize>;
81
82    fn locate<R>(&mut self, records: R) -> Self::IntoIter
83    where
84        R: Records,
85    {
86        if records.count_columns() > 0 {
87            std::iter::once(records.count_columns() - 1)
88        } else {
89            std::iter::once(0)
90        }
91    }
92}
93
94impl<B> Locator for Rows<B>
95where
96    B: RangeBounds<usize>,
97{
98    type Coordinate = usize;
99    type IntoIter = Range<usize>;
100
101    fn locate<R>(&mut self, records: R) -> Self::IntoIter
102    where
103        R: Records,
104    {
105        let (from, to) = bounds_to_usize(
106            self.get_range().start_bound(),
107            self.get_range().end_bound(),
108            records.count_columns(),
109        );
110
111        from..to
112    }
113}
114
115impl Locator for Row {
116    type Coordinate = usize;
117    type IntoIter = Once<usize>;
118
119    fn locate<R>(&mut self, _: R) -> Self::IntoIter
120    where
121        R: Records,
122    {
123        std::iter::once((*self).into())
124    }
125}
126
127impl Locator for FirstRow {
128    type Coordinate = usize;
129    type IntoIter = Once<usize>;
130
131    fn locate<R>(&mut self, _: R) -> Self::IntoIter
132    where
133        R: Records,
134    {
135        std::iter::once(0)
136    }
137}
138
139impl Locator for LastRow {
140    type Coordinate = usize;
141    type IntoIter = Once<usize>;
142
143    fn locate<R>(&mut self, records: R) -> Self::IntoIter
144    where
145        R: Records,
146    {
147        if records.count_rows() > 0 {
148            std::iter::once(records.count_rows() - 1)
149        } else {
150            std::iter::once(0)
151        }
152    }
153}
154
155/// The structure is an implementaion of [`Locator`] to search for a column by it's name.
156/// A name is considerent be a value in a first row.
157///
158/// So even if in reality there's no header, first row will be consideret the one.
159#[derive(Debug, Clone, Copy)]
160pub struct ByColumnName<S>(S);
161
162impl<S> ByColumnName<S> {
163    /// Constructs a new object of the structure.
164    pub fn new(text: S) -> Self
165    where
166        S: AsRef<str>,
167    {
168        Self(text)
169    }
170}
171
172impl<S> Locator for ByColumnName<S>
173where
174    S: AsRef<str>,
175{
176    type Coordinate = usize;
177    type IntoIter = Vec<usize>;
178
179    fn locate<R>(&mut self, records: R) -> Self::IntoIter
180    where
181        R: Records,
182    {
183        // todo: can be optimized by creating Iterator
184        (0..records.count_columns())
185            .filter(|col| records.get_text((0, *col)) == self.0.as_ref())
186            .collect::<Vec<_>>()
187    }
188}
189
190impl<S> Object for ByColumnName<S>
191where
192    S: AsRef<str>,
193{
194    type Iter = std::vec::IntoIter<Entity>;
195
196    fn cells<R>(&self, table: &Table<R>) -> Self::Iter
197    where
198        R: Records,
199    {
200        // todo: can be optimized by creating Iterator
201        (0..table.count_columns())
202            .filter(|col| table.get_records().get_text((0, *col)) == self.0.as_ref())
203            .map(Entity::Column)
204            .collect::<Vec<_>>()
205            .into_iter()
206    }
207}