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