excel_lib/
cell.rs

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// For a cell to represent an hrange or a vrange,
41// utilize a 0 index for the row or column that doens't exist.
42// F.ex. vrange will have column CellIndex > 0 with row CellIndex 0
43#[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    // Top to Bottom, Left to Right
64    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        // Check if vrange
84        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        // Check if hrange
100        } 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}