1use std::fmt;
2use std::cmp::Ordering;
3use std::hash::{Hasher, Hash};
4
5#[derive(Debug, Clone, Copy, Eq)]
6pub struct CellIndex {
7 pub index : usize,
8 pub anchor : bool
9}
10
11impl PartialEq for CellIndex {
12 fn eq(&self, other: &Self) -> bool {
13 self.index == other.index
14 }
15}
16
17impl Hash for CellIndex {
18 fn hash<H: Hasher>(&self, state: &mut H) {
19 self.index.hash(state);
20 }
21}
22
23impl From<usize> for CellIndex {
24 fn from(f: usize) -> CellIndex {
25 CellIndex {
26 index : f,
27 anchor : false
28 }
29 }
30}
31
32impl From<(usize, bool)> for CellIndex {
33 fn from(a : (usize, bool)) -> CellIndex {
34 CellIndex {
35 index : a.0,
36 anchor : a.1
37 }
38 }
39}
40#[derive(Clone, Copy, Eq)]
44pub struct Cell {
45 pub row : CellIndex,
46 pub column : CellIndex
47}
48
49impl Hash for Cell {
50 fn hash<H: Hasher>(&self, state: &mut H) {
51 self.row.hash(state);
52 self.column.hash(state);
53 }
54}
55
56impl PartialOrd for Cell {
57 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
58 Some(self.cmp(other))
59 }
60}
61
62impl Ord for Cell {
63 fn cmp(&self, other: &Self) -> Ordering {
65 if self.column.index == other.column.index {
66 self.row.index.cmp(&other.row.index)
67 } else {
68 self.column.index.cmp(&other.column.index)
69 }
70 }
71}
72
73impl PartialEq for Cell {
74 fn eq(&self, other: &Self) -> bool {
75 (self.row.index == other.row.index) &
76 (self.column.index == other.column.index)
77 }
78}
79
80impl From<String> for Cell {
81 fn from(range : String) -> Cell {
82 let alpha = String::from("abcdefghijklmnopqrstuvwxyz");
83 if range.chars().filter(|c| c.is_numeric()).count() == 0 {
85 let col_anchor : bool = range.starts_with('$');
86 let col_str = range
87 .chars()
88 .filter(|c| c.is_alphabetic())
89 .collect::<String>();
90 let mut col = 0;
91 for (_, c) in col_str.to_lowercase().chars().enumerate() {
92 let c_i = alpha.chars().position(|r| r == c).unwrap();
93 col = col * 26 + c_i + 1;
94 }
95 Cell {
96 row: CellIndex::from((0, false)),
97 column: CellIndex::from((col as usize, col_anchor))
98 }
99 } else if range.chars().filter(|c| c.is_alphabetic()).count() == 0 {
101 let row_anchor : bool = range.starts_with('$');
102 let row_num_str = range.chars().filter(|c| c.is_ascii_digit()).collect::<String>();
103 let row: usize = row_num_str.parse().unwrap();
104 Cell {
105 row: CellIndex::from((row, row_anchor)),
106 column: CellIndex::from((0, false))
107 }
108 } else {
109 let col_anchor : bool = range.starts_with('$');
110 let row_anchor : bool = match col_anchor {
111 true => {
112 range.chars().filter(|c| c == &'$').count() > 1
113 },
114 false => {
115 range.contains('$')
116 }
117 };
118 let col_str = range
119 .chars()
120 .filter(|c| c.is_alphabetic())
121 .collect::<String>();
122 let row_num_str = range.chars().filter(|c| c.is_ascii_digit()).collect::<String>();
123 let row: usize = row_num_str.parse().unwrap();
124 let mut col = 0;
125 for (_, c) in col_str.to_lowercase().chars().enumerate() {
126 let c_i = alpha.chars().position(|r| r == c).unwrap();
127 col = col * 26 + c_i + 1;
128 }
129 Cell {
130 row : CellIndex::from((row, row_anchor)),
131 column : CellIndex::from((col as usize, col_anchor))
132 }
133 }
134 }
135}
136
137impl From<&str> for Cell {
138 fn from(s : &str) -> Cell {
139 Cell::from(s.to_owned())
140 }
141}
142
143impl From<(usize, usize)> for Cell {
144 fn from((a, b) : (usize, usize)) -> Cell {
145 Cell {
146 row : CellIndex::from(a),
147 column : CellIndex::from(b)
148 }
149
150 }
151}
152
153impl Cell {
154 pub fn as_tuple(self) -> (usize, usize) {
155 (self.row.index, self.column.index)
156 }
157
158 pub fn is_hrange(self) -> bool {
159 self.row.index > 0 && self.column.index == 0
160 }
161
162 pub fn is_vrange(self) -> bool {
163 self.column.index > 0 && self.row.index == 0
164 }
165
166 pub fn column_number_to_letter(col_idx: usize) -> String {
167 let alpha = String::from("ABCDEFGHIJKLMNOPQRSTUVWXYZ");
168 let mut col_name: Vec<char> = vec![];
169 let mut n = col_idx;
170 while n > 0 {
171 let rem: usize = n % 26;
172 if rem == 0 {
173 col_name.push('Z');
174 n = (n / 26) - 1;
175 } else {
176 col_name.push(alpha.chars().nth(rem - 1).unwrap());
177 n /= 26;
178 }
179 }
180 col_name.into_iter().rev().collect::<String>()
181 }
182}
183
184impl fmt::Display for Cell {
185 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
186 let mut output = String::new();
187 if self.column.index != 0 {
188 let col_string : String = Cell::column_number_to_letter(self.column.index);
189 if self.column.anchor {
190 output = format!("${}", col_string);
191 } else {
192 output = col_string;
193 }
194 }
195 if self.row.index != 0 {
196 let row_string : String = self.row.index.to_string();
197 if self.row.anchor {
198 output = format!("{}${}", output, row_string);
199 } else {
200 output = format!("{}{}", output, row_string);
201 }
202 }
203 write!(f, "{}", output)
204 }
205}
206
207impl fmt::Debug for Cell {
208 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
209 write!(f, "{}", self)
210 }
211}
212
213#[cfg(test)]
214mod tests {
215 use crate::cell::Cell;
216
217 #[test]
218 fn parse_from_string() {
219 assert_eq!(Cell::from("A1").to_string(), String::from("A1"));
220 assert_eq!(Cell::from("B21").to_string(), String::from("B21"));
221 assert_eq!(Cell::from("AA1").to_string(), String::from("AA1"));
222 assert_eq!(Cell::from("CB100").to_string(), String::from("CB100"));
223 assert_eq!(Cell::from("DD2").to_string(), String::from("DD2"));
224 assert_eq!(Cell::from("AAA10").to_string(), String::from("AAA10"));
225 assert_eq!(Cell::from("GM1").to_string(), String::from("GM1"));
226 assert_eq!(Cell::from("ZZ30").to_string(), String::from("ZZ30"));
227 assert_eq!(Cell::from("KJ15").to_string(), String::from("KJ15"));
228 assert_eq!(Cell::from("A").to_string(), String::from("A"));
229 assert_eq!(Cell::from("12").to_string(), String::from("12"));
230 }
231}