Skip to main content

spreadsheet_mcp/diff/
cells.rs

1use super::address::CellAddress;
2use super::sst::Sst;
3use anyhow::{Result, anyhow};
4use quick_xml::events::{BytesStart, Event};
5use quick_xml::reader::Reader;
6use std::io::BufRead;
7
8#[derive(Debug)]
9pub struct RawCell {
10    pub address: CellAddress,
11    pub value: Option<String>,
12    pub formula: Option<String>,
13    pub style_id: Option<u32>,
14}
15
16pub struct CellIterator<'a, R: BufRead> {
17    reader: Reader<R>,
18    sst: Option<&'a Sst>,
19    buf: Vec<u8>,
20}
21
22impl<'a, R: BufRead> CellIterator<'a, R> {
23    pub fn new(reader: R, sst: Option<&'a Sst>) -> Self {
24        let reader = Reader::from_reader(reader);
25        Self {
26            reader,
27            sst,
28            buf: Vec::new(),
29        }
30    }
31
32    fn read_text_content(&mut self, end_tag: &[u8]) -> Result<String> {
33        let mut text = String::new();
34        let mut buf = Vec::new();
35        loop {
36            match self.reader.read_event_into(&mut buf)? {
37                Event::Text(e) => text.push_str(&e.unescape()?),
38                Event::CData(e) => text.push_str(&String::from_utf8_lossy(&e)),
39                Event::End(e) if e.name().as_ref() == end_tag => break,
40                Event::Eof => return Err(anyhow!("Unexpected EOF reading text")),
41                _ => (),
42            }
43            buf.clear();
44        }
45        Ok(text)
46    }
47
48    fn parse_cell(&mut self, e: &BytesStart) -> Result<RawCell> {
49        let mut address_str = String::new();
50        let mut type_str = String::new();
51        let mut style_id: Option<u32> = None;
52
53        for attr in e.attributes() {
54            let attr = attr?;
55            if attr.key.as_ref() == b"r" {
56                address_str = String::from_utf8_lossy(&attr.value).to_string();
57            } else if attr.key.as_ref() == b"t" {
58                type_str = String::from_utf8_lossy(&attr.value).to_string();
59            } else if attr.key.as_ref() == b"s" {
60                let s = String::from_utf8_lossy(&attr.value).to_string();
61                style_id = s.parse::<u32>().ok();
62            }
63        }
64
65        if address_str.is_empty() {
66            return Err(anyhow!("Cell missing address"));
67        }
68        let address = CellAddress::parse(&address_str)
69            .ok_or_else(|| anyhow!("Invalid cell address: {}", address_str))?;
70
71        let mut value = None;
72        let mut formula = None;
73        let mut buf = Vec::new();
74
75        loop {
76            match self.reader.read_event_into(&mut buf) {
77                Ok(Event::Start(ref e)) => {
78                    match e.name().as_ref() {
79                        b"v" => {
80                            let text = self.read_text_content(b"v")?;
81                            value = Some(text);
82                        }
83                        b"f" => {
84                            let text = self.read_text_content(b"f")?;
85                            formula = Some(text);
86                        }
87                        b"is" => {
88                            // Inline string - just skip for now or try to read text if simple
89                            // For now we might lose inline strings, but they are rare in recalc scenarios
90                        }
91                        _ => {}
92                    }
93                }
94                Ok(Event::End(ref e)) if e.name().as_ref() == b"c" => {
95                    break;
96                }
97                Ok(Event::Eof) => break,
98                Err(e) => return Err(e.into()),
99                _ => {}
100            }
101            buf.clear();
102        }
103
104        // Post-process value based on type
105        if type_str == "s"
106            && let Some(ref v) = value
107            && let Ok(idx) = v.parse::<usize>()
108            && let Some(sst) = self.sst
109            && let Some(s) = sst.get(idx)
110        {
111            value = Some(s.to_string());
112        }
113
114        Ok(RawCell {
115            address,
116            value,
117            formula,
118            style_id,
119        })
120    }
121}
122
123impl<'a, R: BufRead> Iterator for CellIterator<'a, R> {
124    type Item = Result<RawCell>;
125
126    fn next(&mut self) -> Option<Self::Item> {
127        loop {
128            self.buf.clear();
129            match self.reader.read_event_into(&mut self.buf) {
130                Ok(Event::Start(ref e)) if e.name().as_ref() == b"c" => {
131                    // Clone event to own the data while parsing children
132                    let e_owned = e.to_owned();
133                    return Some(self.parse_cell(&e_owned));
134                }
135                Ok(Event::Eof) => return None,
136                Err(e) => return Some(Err(e.into())),
137                _ => {}
138            }
139        }
140    }
141}