1use std::fmt;
2use std::sync::{Arc, OnceLock};
3
4use crate::{CellAddress, FormulaCell, ValidationError};
5
6#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
8pub enum NumberFormatKind {
9 #[default]
11 General,
12 Number,
14 Date,
16 Time,
18 DateTime,
20 Duration,
22}
23
24#[derive(Debug, Clone, PartialEq, Eq, Hash, Default)]
26pub struct NumberFormat {
27 id: u32,
28 code: Option<Box<str>>,
29 kind: NumberFormatKind,
30}
31
32impl NumberFormat {
33 pub fn built_in(id: u32, kind: NumberFormatKind) -> Result<Self, ValidationError> {
40 if id >= 164 {
41 return Err(ValidationError::BuiltInNumberFormatId { value: id });
42 }
43 Ok(Self {
44 id,
45 code: None,
46 kind,
47 })
48 }
49
50 pub fn custom(
57 id: u32,
58 code: impl Into<String>,
59 kind: NumberFormatKind,
60 ) -> Result<Self, ValidationError> {
61 if id < 164 {
62 return Err(ValidationError::CustomNumberFormatId { value: id });
63 }
64 let code = code.into();
65 if code.is_empty() {
66 return Err(ValidationError::NumberFormatCodeEmpty);
67 }
68 Ok(Self {
69 id,
70 code: Some(code.into_boxed_str()),
71 kind,
72 })
73 }
74
75 pub(crate) fn new(id: u32, code: Option<Box<str>>, kind: NumberFormatKind) -> Self {
76 Self { id, code, kind }
77 }
78
79 pub const fn id(&self) -> u32 {
81 self.id
82 }
83
84 pub fn code(&self) -> Option<&str> {
86 self.code.as_deref()
87 }
88
89 pub const fn kind(&self) -> NumberFormatKind {
91 self.kind
92 }
93}
94
95#[derive(Debug, Clone, Copy, PartialEq, PartialOrd)]
97pub struct FiniteNumber(f64);
98
99impl FiniteNumber {
100 pub fn new(value: f64) -> Result<Self, ValidationError> {
106 if !value.is_finite() {
107 return Err(ValidationError::NonFiniteNumber);
108 }
109 Ok(Self(value))
110 }
111
112 pub const fn get(self) -> f64 {
114 self.0
115 }
116}
117
118impl TryFrom<f64> for FiniteNumber {
119 type Error = ValidationError;
120
121 fn try_from(value: f64) -> Result<Self, Self::Error> {
122 Self::new(value)
123 }
124}
125
126#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
128#[non_exhaustive]
129pub enum ExcelError {
130 Null,
132 DivisionByZero,
134 Value,
136 Reference,
138 Name,
140 Number,
142 NotAvailable,
144 GettingData,
146 Spill,
148 Calculation,
150}
151
152impl ExcelError {
153 pub const fn as_str(self) -> &'static str {
155 match self {
156 Self::Null => "#NULL!",
157 Self::DivisionByZero => "#DIV/0!",
158 Self::Value => "#VALUE!",
159 Self::Reference => "#REF!",
160 Self::Name => "#NAME?",
161 Self::Number => "#NUM!",
162 Self::NotAvailable => "#N/A",
163 Self::GettingData => "#GETTING_DATA",
164 Self::Spill => "#SPILL!",
165 Self::Calculation => "#CALC!",
166 }
167 }
168}
169
170impl fmt::Display for ExcelError {
171 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
172 formatter.write_str(self.as_str())
173 }
174}
175
176#[derive(Debug, Clone, PartialEq, Default)]
178#[non_exhaustive]
179pub enum CellValue {
180 #[default]
182 Blank,
183 Number(FiniteNumber),
185 Text(String),
187 Logical(bool),
189 Error(ExcelError),
191}
192
193impl CellValue {
194 pub fn number(value: f64) -> Result<Self, ValidationError> {
200 Ok(Self::Number(FiniteNumber::new(value)?))
201 }
202}
203
204#[derive(Debug, Clone, PartialEq)]
206pub enum CellContent {
207 Literal(CellValue),
209 Formula(FormulaCell),
211}
212
213#[derive(Debug, Clone, PartialEq)]
215pub struct Cell {
216 address: CellAddress,
217 content: Arc<CellContent>,
218 number_format: Arc<NumberFormat>,
219}
220
221fn shared_number_format(number_format: NumberFormat) -> Arc<NumberFormat> {
222 static DEFAULT: OnceLock<Arc<NumberFormat>> = OnceLock::new();
223
224 if number_format == NumberFormat::default() {
225 Arc::clone(DEFAULT.get_or_init(|| Arc::new(NumberFormat::default())))
226 } else {
227 Arc::new(number_format)
228 }
229}
230
231impl Cell {
232 pub fn new(address: CellAddress, content: CellContent) -> Self {
234 Self {
235 address,
236 content: Arc::new(content),
237 number_format: shared_number_format(NumberFormat::default()),
238 }
239 }
240
241 pub(crate) fn with_number_format(
242 address: CellAddress,
243 content: CellContent,
244 number_format: NumberFormat,
245 ) -> Self {
246 Self {
247 address,
248 content: Arc::new(content),
249 number_format: shared_number_format(number_format),
250 }
251 }
252
253 pub const fn address(&self) -> CellAddress {
255 self.address
256 }
257
258 pub fn content(&self) -> &CellContent {
260 self.content.as_ref()
261 }
262
263 pub fn number_format(&self) -> &NumberFormat {
265 self.number_format.as_ref()
266 }
267
268 pub(crate) fn with_content_and_number_format(
269 address: CellAddress,
270 content: CellContent,
271 number_format: NumberFormat,
272 ) -> Self {
273 Self {
274 address,
275 content: Arc::new(content),
276 number_format: shared_number_format(number_format),
277 }
278 }
279
280 pub(crate) fn with_replaced_content(&self, content: CellContent) -> Self {
281 Self {
282 address: self.address,
283 content: Arc::new(content),
284 number_format: Arc::clone(&self.number_format),
285 }
286 }
287
288 pub(crate) fn with_replaced_number_format(&self, number_format: NumberFormat) -> Self {
289 Self {
290 address: self.address,
291 content: Arc::clone(&self.content),
292 number_format: shared_number_format(number_format),
293 }
294 }
295
296 #[cfg(test)]
297 pub(crate) fn shares_content_with(&self, other: &Self) -> bool {
298 Arc::ptr_eq(&self.content, &other.content)
299 }
300
301 #[cfg(test)]
302 pub(crate) fn shares_number_format_with(&self, other: &Self) -> bool {
303 Arc::ptr_eq(&self.number_format, &other.number_format)
304 }
305}
306
307#[cfg(test)]
308mod tests {
309 use super::*;
310
311 #[test]
312 fn clone_and_metadata_edit_share_large_immutable_payloads() {
313 let original = Cell::with_number_format(
314 CellAddress::from_a1("A1").expect("cell address"),
315 CellContent::Literal(CellValue::Text("x".repeat(8_192))),
316 NumberFormat::custom(164, "0.000", NumberFormatKind::Number).expect("format"),
317 );
318 let cloned = original.clone();
319 assert!(original.shares_content_with(&cloned));
320 assert!(original.shares_number_format_with(&cloned));
321
322 let reformatted = original.with_replaced_number_format(NumberFormat::default());
323 assert!(original.shares_content_with(&reformatted));
324 assert!(!original.shares_number_format_with(&reformatted));
325 }
326}