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))]
137#[cfg_attr(feature = "json", serde(rename_all = "lowercase"))]
138#[derive(Clone, Debug, PartialEq, Eq, Hash, Default)]
139pub enum DefinedNameScope {
140 #[default]
141 Workbook,
142 Sheet,
143}
144
145#[cfg_attr(feature = "json", derive(Serialize, Deserialize))]
146#[cfg_attr(feature = "json", serde(tag = "type", rename_all = "lowercase"))]
147#[derive(Clone, Debug, PartialEq)]
148pub enum DefinedNameDefinition {
149 Range { address: RangeAddress },
150 Literal { value: LiteralValue },
151}
152
153#[cfg_attr(feature = "json", derive(Serialize, Deserialize))]
154#[derive(Clone, Debug, PartialEq)]
155pub struct DefinedName {
156 pub name: String,
157
158 #[cfg_attr(feature = "json", serde(default))]
159 pub scope: DefinedNameScope,
160
161 #[cfg_attr(
165 feature = "json",
166 serde(default, skip_serializing_if = "Option::is_none")
167 )]
168 pub scope_sheet: Option<String>,
169
170 pub definition: DefinedNameDefinition,
171}
172
173#[cfg_attr(feature = "json", derive(Serialize, Deserialize))]
174#[derive(Clone, Debug)]
175pub struct TableDefinition {
176 pub name: String,
177 pub range: (u32, u32, u32, u32),
178 #[cfg_attr(feature = "json", serde(default = "default_true"))]
185 pub header_row: bool,
186 pub headers: Vec<String>,
187 pub totals_row: bool,
188}
189
190#[cfg(feature = "json")]
191fn default_true() -> bool {
192 true
193}
194
195#[cfg_attr(feature = "json", derive(Serialize, Deserialize))]
196#[derive(Clone, Debug)]
197pub struct MergedRange {
198 pub start_row: u32,
199 pub start_col: u32,
200 pub end_row: u32,
201 pub end_col: u32,
202}
203
204impl MergedRange {
205 pub fn contains(&self, row: u32, col: u32) -> bool {
206 row >= self.start_row && row <= self.end_row && col >= self.start_col && col <= self.end_col
207 }
208}
209
210#[derive(Clone, Copy, Debug)]
211pub enum AccessGranularity {
212 Cell, Range, Sheet, Workbook, }
217
218#[derive(Clone, Debug)]
219pub enum LoadStrategy {
220 EagerAll,
222
223 EagerSheet,
225
226 LazyRange { row_chunk: usize, col_chunk: usize },
228
229 LazyCell,
231
232 WriteOnly,
234}
235
236pub trait SpreadsheetReader: Send + Sync {
237 type Error: std::error::Error + Send + Sync + 'static;
238
239 fn access_granularity(&self) -> AccessGranularity;
240 fn capabilities(&self) -> BackendCaps;
241 fn sheet_names(&self) -> Result<Vec<String>, Self::Error>;
242
243 fn defined_names(&mut self) -> Result<Vec<DefinedName>, Self::Error> {
247 Ok(Vec::new())
248 }
249
250 fn open_path<P: AsRef<Path>>(path: P) -> Result<Self, Self::Error>
252 where
253 Self: Sized;
254
255 fn open_reader(reader: Box<dyn Read + Send + Sync>) -> Result<Self, Self::Error>
256 where
257 Self: Sized;
258
259 fn open_bytes(data: Vec<u8>) -> Result<Self, Self::Error>
260 where
261 Self: Sized;
262
263 fn read_cell(
264 &mut self,
265 sheet: &str,
266 row: u32,
267 col: u32,
268 ) -> Result<Option<CellData>, Self::Error> {
269 let mut range = self.read_range(sheet, (row, col), (row, col))?;
271 Ok(range.remove(&(row, col)))
272 }
273
274 fn read_range(
275 &mut self,
276 sheet: &str,
277 start: (u32, u32),
278 end: (u32, u32),
279 ) -> Result<BTreeMap<(u32, u32), CellData>, Self::Error>;
280
281 fn read_sheet(&mut self, sheet: &str) -> Result<SheetData, Self::Error>;
282
283 fn sheet_bounds(&self, sheet: &str) -> Option<(u32, u32)>;
284 fn is_loaded(&self, sheet: &str, row: Option<u32>, col: Option<u32>) -> bool;
285}
286
287pub trait SpreadsheetWriter: Send + Sync {
288 type Error: std::error::Error + Send + Sync + 'static;
289
290 fn write_cell(
291 &mut self,
292 sheet: &str,
293 row: u32,
294 col: u32,
295 data: CellData,
296 ) -> Result<(), Self::Error>;
297
298 fn write_range(
299 &mut self,
300 sheet: &str,
301 cells: BTreeMap<(u32, u32), CellData>,
302 ) -> Result<(), Self::Error>;
303
304 fn clear_range(
305 &mut self,
306 sheet: &str,
307 start: (u32, u32),
308 end: (u32, u32),
309 ) -> Result<(), Self::Error>;
310
311 fn create_sheet(&mut self, name: &str) -> Result<(), Self::Error>;
312 fn delete_sheet(&mut self, name: &str) -> Result<(), Self::Error>;
313 fn rename_sheet(&mut self, old: &str, new: &str) -> Result<(), Self::Error>;
314
315 fn flush(&mut self) -> Result<(), Self::Error>;
316 fn save(&mut self) -> Result<(), Self::Error> {
317 self.save_to(SaveDestination::InPlace).map(|_| ())
318 }
319
320 fn save_to<'a>(&mut self, dest: SaveDestination<'a>) -> Result<Option<Vec<u8>>, Self::Error> {
323 let _ = dest;
324 unreachable!("save_to must be implemented by writer backends that expose persistence");
325 }
326
327 fn save_as_path<P: AsRef<std::path::Path>>(&mut self, path: P) -> Result<(), Self::Error> {
328 self.save_to(SaveDestination::Path(path.as_ref()))
329 .map(|_| ())
330 }
331
332 fn save_to_bytes(&mut self) -> Result<Vec<u8>, Self::Error> {
333 self.save_to(SaveDestination::Bytes)
334 .map(|opt| opt.unwrap_or_default())
335 }
336
337 fn write_to<W: Write>(&mut self, writer: &mut W) -> Result<(), Self::Error> {
338 self.save_to(SaveDestination::Writer(writer)).map(|_| ())
339 }
340}
341
342pub enum SaveDestination<'a> {
344 InPlace, Path(&'a std::path::Path), Writer(&'a mut dyn Write), Bytes, }
349
350pub trait SpreadsheetIO: SpreadsheetReader + SpreadsheetWriter {}