spreadsheet_mcp/diff/
address.rs1use std::cmp::Ordering;
2
3#[derive(Debug, PartialEq, Eq, Clone)]
4pub struct CellAddress {
5 pub col: u32,
6 pub row: u32,
7 pub original: String,
8}
9
10impl CellAddress {
11 pub fn parse(s: &str) -> Option<Self> {
12 let split_idx = s.find(|c: char| c.is_ascii_digit())?;
14 let (col_str, row_str) = s.split_at(split_idx);
15
16 let row = row_str.parse::<u32>().ok()?;
17 let col = col_from_letters(col_str)?;
18
19 Some(Self {
20 col,
21 row,
22 original: s.to_string(),
23 })
24 }
25}
26
27fn col_from_letters(s: &str) -> Option<u32> {
28 let mut col = 0;
29 for c in s.chars() {
30 if !c.is_ascii_alphabetic() {
31 return None;
32 }
33 col = col * 26 + (c.to_ascii_uppercase() as u32 - 'A' as u32 + 1);
34 }
35 Some(col)
36}
37
38impl Ord for CellAddress {
39 fn cmp(&self, other: &Self) -> Ordering {
40 match self.row.cmp(&other.row) {
42 Ordering::Equal => self.col.cmp(&other.col),
43 ord => ord,
44 }
45 }
46}
47
48impl PartialOrd for CellAddress {
49 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
50 Some(self.cmp(other))
51 }
52}
53
54#[cfg(test)]
55mod tests {
56 use super::*;
57
58 #[test]
59 fn test_ordering() {
60 let a1 = CellAddress::parse("A1").unwrap();
61 let b1 = CellAddress::parse("B1").unwrap();
62 let a2 = CellAddress::parse("A2").unwrap();
63 let aa1 = CellAddress::parse("AA1").unwrap();
64
65 assert!(a1 < b1);
66 assert!(b1 < aa1); assert!(aa1 < a2); }
69}