1use formualizer_common::{LiteralValue, RangeAddress};
2#[cfg(feature = "json")]
3use serde::{Deserialize, Serialize};
4use std::collections::BTreeMap;
5use std::io::{Read, Write};
6use std::path::Path;
7
8#[derive(Clone, Debug)]
9pub struct CellData {
10 pub value: Option<LiteralValue>,
11 pub formula: Option<String>,
12 pub style: Option<StyleId>,
13}
14
15impl CellData {
16 pub fn from_value<V: IntoLiteral>(value: V) -> Self {
17 Self {
18 value: Some(value.into_literal()),
19 formula: None,
20 style: None,
21 }
22 }
23
24 pub fn from_formula(formula: impl Into<String>) -> Self {
25 Self {
26 value: None,
27 formula: Some(formula.into()),
28 style: None,
29 }
30 }
31}
32
33pub trait IntoLiteral {
35 fn into_literal(self) -> LiteralValue;
36}
37
38impl IntoLiteral for LiteralValue {
39 fn into_literal(self) -> LiteralValue {
40 self
41 }
42}
43
44impl IntoLiteral for f64 {
45 fn into_literal(self) -> LiteralValue {
46 LiteralValue::Number(self)
47 }
48}
49
50impl IntoLiteral for i64 {
51 fn into_literal(self) -> LiteralValue {
52 LiteralValue::Int(self)
53 }
54}
55
56impl IntoLiteral for i32 {
57 fn into_literal(self) -> LiteralValue {
58 LiteralValue::Int(self as i64)
59 }
60}
61
62impl IntoLiteral for bool {
63 fn into_literal(self) -> LiteralValue {
64 LiteralValue::Boolean(self)
65 }
66}
67
68impl IntoLiteral for String {
69 fn into_literal(self) -> LiteralValue {
70 LiteralValue::Text(self)
71 }
72}
73
74impl IntoLiteral for &str {
75 fn into_literal(self) -> LiteralValue {
76 LiteralValue::Text(self.to_string())
77 }
78}
79
80pub type StyleId = u32;
81
82#[derive(Clone, Debug, Default)]
83pub struct BackendCaps {
84 pub read: bool,
85 pub write: bool,
86 pub streaming: bool,
87 pub tables: bool,
88 pub named_ranges: bool,
89 pub formulas: bool,
90 pub styles: bool,
91 pub lazy_loading: bool,
92 pub random_access: bool,
93 pub bytes_input: bool,
94
95 pub date_system_1904: bool,
97 pub merged_cells: bool,
98 pub rich_text: bool,
99 pub hyperlinks: bool,
100 pub data_validations: bool,
101 pub shared_formulas: bool,
102}
103
104#[derive(Clone, Debug)]
105pub struct SheetData {
106 pub cells: BTreeMap<(u32, u32), CellData>,
107 pub dimensions: Option<(u32, u32)>,
108 pub tables: Vec<TableDefinition>,
109 pub named_ranges: Vec<NamedRange>,
110 pub date_system_1904: bool,
111 pub merged_cells: Vec<MergedRange>,
112 pub hidden: bool,
113}
114
115#[cfg_attr(feature = "json", derive(Serialize, Deserialize))]
116#[cfg_attr(feature = "json", serde(rename_all = "lowercase"))]
117#[derive(Clone, Debug, PartialEq, Eq, Hash, Default)]
118pub enum NamedRangeScope {
119 #[default]
120 Workbook,
121 Sheet,
122}
123
124#[cfg_attr(feature = "json", derive(Serialize, Deserialize))]
125#[derive(Clone, Debug)]
126pub struct NamedRange {
127 pub name: String,
128 #[cfg_attr(feature = "json", serde(default))]
129 pub scope: NamedRangeScope,
130 pub address: RangeAddress,
131}
132
133#[cfg_attr(feature = "json", derive(Serialize, Deserialize))]
134#[derive(Clone, Debug)]
135pub struct TableDefinition {
136 pub name: String,
137 pub range: (u32, u32, u32, u32),
138 pub headers: Vec<String>,
139 pub totals_row: bool,
140}
141
142#[cfg_attr(feature = "json", derive(Serialize, Deserialize))]
143#[derive(Clone, Debug)]
144pub struct MergedRange {
145 pub start_row: u32,
146 pub start_col: u32,
147 pub end_row: u32,
148 pub end_col: u32,
149}
150
151impl MergedRange {
152 pub fn contains(&self, row: u32, col: u32) -> bool {
153 row >= self.start_row && row <= self.end_row && col >= self.start_col && col <= self.end_col
154 }
155}
156
157#[derive(Clone, Copy, Debug)]
158pub enum AccessGranularity {
159 Cell, Range, Sheet, Workbook, }
164
165#[derive(Clone, Debug)]
166pub enum LoadStrategy {
167 EagerAll,
169
170 EagerSheet,
172
173 LazyRange { row_chunk: usize, col_chunk: usize },
175
176 LazyCell,
178
179 WriteOnly,
181}
182
183pub trait SpreadsheetReader: Send + Sync {
184 type Error: std::error::Error + Send + Sync + 'static;
185
186 fn access_granularity(&self) -> AccessGranularity;
187 fn capabilities(&self) -> BackendCaps;
188 fn sheet_names(&self) -> Result<Vec<String>, Self::Error>;
189
190 fn open_path<P: AsRef<Path>>(path: P) -> Result<Self, Self::Error>
192 where
193 Self: Sized;
194
195 fn open_reader(reader: Box<dyn Read + Send + Sync>) -> Result<Self, Self::Error>
196 where
197 Self: Sized;
198
199 fn open_bytes(data: Vec<u8>) -> Result<Self, Self::Error>
200 where
201 Self: Sized;
202
203 fn read_cell(
204 &mut self,
205 sheet: &str,
206 row: u32,
207 col: u32,
208 ) -> Result<Option<CellData>, Self::Error> {
209 let mut range = self.read_range(sheet, (row, col), (row, col))?;
211 Ok(range.remove(&(row, col)))
212 }
213
214 fn read_range(
215 &mut self,
216 sheet: &str,
217 start: (u32, u32),
218 end: (u32, u32),
219 ) -> Result<BTreeMap<(u32, u32), CellData>, Self::Error>;
220
221 fn read_sheet(&mut self, sheet: &str) -> Result<SheetData, Self::Error>;
222
223 fn sheet_bounds(&self, sheet: &str) -> Option<(u32, u32)>;
224 fn is_loaded(&self, sheet: &str, row: Option<u32>, col: Option<u32>) -> bool;
225}
226
227pub trait SpreadsheetWriter: Send + Sync {
228 type Error: std::error::Error + Send + Sync + 'static;
229
230 fn write_cell(
231 &mut self,
232 sheet: &str,
233 row: u32,
234 col: u32,
235 data: CellData,
236 ) -> Result<(), Self::Error>;
237
238 fn write_range(
239 &mut self,
240 sheet: &str,
241 cells: BTreeMap<(u32, u32), CellData>,
242 ) -> Result<(), Self::Error>;
243
244 fn clear_range(
245 &mut self,
246 sheet: &str,
247 start: (u32, u32),
248 end: (u32, u32),
249 ) -> Result<(), Self::Error>;
250
251 fn create_sheet(&mut self, name: &str) -> Result<(), Self::Error>;
252 fn delete_sheet(&mut self, name: &str) -> Result<(), Self::Error>;
253 fn rename_sheet(&mut self, old: &str, new: &str) -> Result<(), Self::Error>;
254
255 fn flush(&mut self) -> Result<(), Self::Error>;
256 fn save(&mut self) -> Result<(), Self::Error> {
257 self.save_to(SaveDestination::InPlace).map(|_| ())
258 }
259
260 fn save_to<'a>(&mut self, dest: SaveDestination<'a>) -> Result<Option<Vec<u8>>, Self::Error> {
263 let _ = dest;
264 unreachable!("save_to must be implemented by writer backends that expose persistence");
265 }
266
267 fn save_as_path<P: AsRef<std::path::Path>>(&mut self, path: P) -> Result<(), Self::Error> {
268 self.save_to(SaveDestination::Path(path.as_ref()))
269 .map(|_| ())
270 }
271
272 fn save_to_bytes(&mut self) -> Result<Vec<u8>, Self::Error> {
273 self.save_to(SaveDestination::Bytes)
274 .map(|opt| opt.unwrap_or_default())
275 }
276
277 fn write_to<W: Write>(&mut self, writer: &mut W) -> Result<(), Self::Error> {
278 self.save_to(SaveDestination::Writer(writer)).map(|_| ())
279 }
280}
281
282pub enum SaveDestination<'a> {
284 InPlace, Path(&'a std::path::Path), Writer(&'a mut dyn Write), Bytes, }
289
290pub trait SpreadsheetIO: SpreadsheetReader + SpreadsheetWriter {}