1use chrono::NaiveTime;
2use std::{
3 fmt::{self, Display},
4 hash::{Hash, Hasher},
5};
6
7use crate::ExcelError;
8
9pub use crate::date_serial::{datetime_to_serial, serial_to_datetime};
10
11#[cfg(feature = "serde")]
12use serde::{Deserialize, Serialize};
13
14#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
15pub enum DateSystem {
16 Excel1900,
17 Excel1904,
18}
19
20impl Display for DateSystem {
21 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
22 match self {
23 DateSystem::Excel1900 => write!(f, "1900"),
24 DateSystem::Excel1904 => write!(f, "1904"),
25 }
26 }
27}
28
29#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
32#[derive(Debug, Clone, PartialEq)]
33pub enum LiteralValue {
34 Int(i64),
35 Number(f64),
36 Text(String),
37 Boolean(bool),
38 Array(Vec<Vec<LiteralValue>>), Date(chrono::NaiveDate), DateTime(chrono::NaiveDateTime), Time(chrono::NaiveTime), Duration(chrono::Duration), Empty, Pending, Error(ExcelError),
47}
48
49impl Hash for LiteralValue {
50 fn hash<H: Hasher>(&self, state: &mut H) {
51 match self {
52 LiteralValue::Int(i) => i.hash(state),
53 LiteralValue::Number(n) => n.to_bits().hash(state),
54 LiteralValue::Text(s) => s.hash(state),
55 LiteralValue::Boolean(b) => b.hash(state),
56 LiteralValue::Array(a) => a.hash(state),
57 LiteralValue::Date(d) => d.hash(state),
58 LiteralValue::DateTime(dt) => dt.hash(state),
59 LiteralValue::Time(t) => t.hash(state),
60 LiteralValue::Duration(d) => d.hash(state),
61 LiteralValue::Empty => state.write_u8(0),
62 LiteralValue::Pending => state.write_u8(1),
63 LiteralValue::Error(e) => e.hash(state),
64 }
65 }
66}
67
68impl Eq for LiteralValue {}
69
70impl Display for LiteralValue {
71 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
72 match self {
73 LiteralValue::Int(i) => write!(f, "{i}"),
74 LiteralValue::Number(n) => write!(f, "{n}"),
75 LiteralValue::Text(s) => write!(f, "{s}"),
76 LiteralValue::Boolean(b) => write!(f, "{b}"),
77 LiteralValue::Error(e) => write!(f, "{e}"),
78 LiteralValue::Array(a) => write!(f, "{a:?}"),
79 LiteralValue::Date(d) => write!(f, "{d}"),
80 LiteralValue::DateTime(dt) => write!(f, "{dt}"),
81 LiteralValue::Time(t) => write!(f, "{t}"),
82 LiteralValue::Duration(d) => write!(f, "{d}"),
83 LiteralValue::Empty => write!(f, ""),
84 LiteralValue::Pending => write!(f, "Pending"),
85 }
86 }
87}
88
89#[non_exhaustive]
90#[derive(Debug, Clone, PartialEq)]
91pub enum ValueError {
92 ImplicitIntersection(String),
93}
94
95impl LiteralValue {
96 #[inline]
101 pub fn finite_or_num_error(self) -> Self {
102 match self {
103 LiteralValue::Number(n) if !n.is_finite() => Self::non_finite_number(),
104 other => other,
105 }
106 }
107
108 #[cold]
109 fn non_finite_number() -> Self {
110 LiteralValue::Error(ExcelError::new(crate::error::ExcelErrorKind::Num))
111 }
112
113 pub fn coerce_to_single_value(&self) -> Result<LiteralValue, ValueError> {
115 match self {
116 LiteralValue::Array(arr) => {
117 if arr.len() == 1 && arr[0].len() == 1 {
120 Ok(arr[0][0].clone())
121 } else if arr.is_empty() || arr[0].is_empty() {
122 Ok(LiteralValue::Empty) } else {
124 Err(ValueError::ImplicitIntersection(
125 "#LiteralValue! Implicit intersection failed".to_string(),
126 ))
127 }
128 }
129 _ => Ok(self.clone()),
130 }
131 }
132
133 pub fn as_serial_number_for(&self, system: DateSystem) -> Option<f64> {
135 match self {
136 LiteralValue::Date(d) => Some(crate::date_to_serial_for(system, d)),
137 LiteralValue::DateTime(dt) => Some(crate::datetime_to_serial_for(system, dt)),
138 LiteralValue::Time(t) => Some(crate::time_to_fraction(t)),
139 LiteralValue::Duration(d) => Some(d.num_seconds() as f64 / 86_400.0),
140 LiteralValue::Int(i) => Some(*i as f64),
141 LiteralValue::Number(n) => Some(*n),
142 LiteralValue::Boolean(b) => Some(if *b { 1.0 } else { 0.0 }),
143 _ => None,
144 }
145 }
146
147 pub fn as_serial_number(&self) -> Option<f64> {
149 self.as_serial_number_for(DateSystem::Excel1900)
150 }
151
152 pub fn try_from_serial_number_for(system: DateSystem, serial: f64) -> Result<Self, ExcelError> {
159 let dt = crate::try_serial_to_datetime_for(system, serial)?;
160 if dt.time() == NaiveTime::from_hms_opt(0, 0, 0).unwrap() {
161 Ok(LiteralValue::Date(dt.date()))
162 } else {
163 Ok(LiteralValue::DateTime(dt))
164 }
165 }
166
167 pub fn try_from_serial_number(serial: f64) -> Result<Self, ExcelError> {
169 Self::try_from_serial_number_for(DateSystem::Excel1900, serial)
170 }
171
172 pub fn from_serial_number(serial: f64) -> Self {
178 let dt = crate::serial_to_datetime(serial);
179 if dt.time() == NaiveTime::from_hms_opt(0, 0, 0).unwrap() {
180 LiteralValue::Date(dt.date())
181 } else {
182 LiteralValue::DateTime(dt)
183 }
184 }
185
186 pub fn is_truthy(&self) -> bool {
187 match self {
188 LiteralValue::Boolean(b) => *b,
189 LiteralValue::Int(i) => *i != 0,
190 LiteralValue::Number(n) => *n != 0.0,
191 LiteralValue::Text(s) => !s.is_empty(),
192 LiteralValue::Array(arr) => !arr.is_empty(),
193 LiteralValue::Date(_) => true,
194 LiteralValue::DateTime(_) => true,
195 LiteralValue::Time(_) => true,
196 LiteralValue::Duration(_) => true,
197 LiteralValue::Error(_) => false,
198 LiteralValue::Empty => false,
199 LiteralValue::Pending => false,
200 }
201 }
202}
203
204#[cfg(test)]
205mod tests {
206 use super::*;
207 use chrono::NaiveDate;
208
209 #[test]
210 fn literal_temporal_values_use_the_selected_date_system() {
211 let date = NaiveDate::from_ymd_opt(2024, 1, 15).unwrap();
212 let value = LiteralValue::Date(date);
213 assert_eq!(
214 value.as_serial_number_for(DateSystem::Excel1900),
215 Some(45_306.0)
216 );
217 assert_eq!(
218 value.as_serial_number_for(DateSystem::Excel1904),
219 Some(43_844.0)
220 );
221 assert_eq!(value.as_serial_number(), Some(45_306.0));
222 }
223
224 #[test]
225 fn literal_serial_construction_is_checked_and_system_aware() {
226 let date = NaiveDate::from_ymd_opt(2024, 1, 15).unwrap();
227 for (system, serial) in [
228 (DateSystem::Excel1900, 45_306.0),
229 (DateSystem::Excel1904, 43_844.0),
230 ] {
231 assert_eq!(
232 LiteralValue::try_from_serial_number_for(system, serial).unwrap(),
233 LiteralValue::Date(date)
234 );
235 }
236 assert!(LiteralValue::try_from_serial_number_for(DateSystem::Excel1900, -1.0).is_err());
237 assert_eq!(
238 LiteralValue::from_serial_number(45_306.0),
239 LiteralValue::Date(date)
240 );
241 assert!(LiteralValue::try_from_serial_number(-1.0).is_err());
242 assert_eq!(
243 LiteralValue::from_serial_number(-1.0),
244 LiteralValue::Date(NaiveDate::from_ymd_opt(1899, 12, 30).unwrap())
245 );
246 }
247}