1use std::hash::{Hash, Hasher};
2
3use serde::de::Error as _;
4use serde::ser::{Error as _, SerializeMap};
5use serde::{Deserialize, Deserializer, Serialize, Serializer};
6use truecalc_core::types::zoned::parse_rfc9557;
7use truecalc_core::types::ZonedInstant;
8
9#[derive(Debug, Clone)]
25pub enum Value {
26 Number(f64),
28 Text(String),
30 Boolean(bool),
32 Error(String),
35 ErrorMsg(String, String),
44 Empty,
48 Array(Vec<Vec<Value>>),
51 Date(f64),
54 Zoned(Box<ZonedInstant>),
57}
58
59fn normalized_bits(x: f64) -> u64 {
63 if x == 0.0 {
64 0.0_f64.to_bits()
65 } else {
66 x.to_bits()
67 }
68}
69
70impl Value {
71 fn error_code(&self) -> Option<&str> {
74 match self {
75 Value::Error(code) | Value::ErrorMsg(code, _) => Some(code.as_str()),
76 _ => None,
77 }
78 }
79
80 pub fn error_message(&self) -> Option<&str> {
83 match self {
84 Value::ErrorMsg(_, msg) => Some(msg.as_str()),
85 _ => None,
86 }
87 }
88}
89
90impl PartialEq for Value {
96 fn eq(&self, other: &Self) -> bool {
97 match (self, other) {
98 (Value::Number(a), Value::Number(b)) => a == b,
99 (Value::Text(a), Value::Text(b)) => a == b,
100 (Value::Boolean(a), Value::Boolean(b)) => a == b,
101 (Value::Empty, Value::Empty) => true,
102 (Value::Array(a), Value::Array(b)) => a == b,
103 (Value::Date(a), Value::Date(b)) => a == b,
104 (Value::Zoned(a), Value::Zoned(b)) => a == b,
105 _ => match (self.error_code(), other.error_code()) {
106 (Some(a), Some(b)) => a == b,
107 _ => false,
108 },
109 }
110 }
111}
112
113impl Hash for Value {
114 fn hash<H: Hasher>(&self, state: &mut H) {
115 if let Some(code) = self.error_code() {
119 "error".hash(state);
120 code.hash(state);
121 return;
122 }
123 std::mem::discriminant(self).hash(state);
124 match self {
125 Value::Number(n) | Value::Date(n) => normalized_bits(*n).hash(state),
126 Value::Text(s) => s.hash(state),
127 Value::Boolean(b) => b.hash(state),
128 Value::Zoned(z) => z.to_rfc9557().hash(state),
131 Value::Empty => {}
132 Value::Array(rows) => {
133 rows.len().hash(state);
134 for row in rows {
135 row.len().hash(state);
136 for v in row {
137 v.hash(state);
138 }
139 }
140 }
141 Value::Error(_) | Value::ErrorMsg(_, _) => unreachable!(),
143 }
144 }
145}
146
147fn serialize_tagged_number<S: Serializer>(
148 kind: &'static str,
149 n: f64,
150 serializer: S,
151) -> Result<S::Ok, S::Error> {
152 if !n.is_finite() {
153 return Err(S::Error::custom(format!(
154 "non-finite {kind} value cannot be serialized (schema spec §8)"
155 )));
156 }
157 let n = if n == 0.0 { 0.0 } else { n };
158 let mut map = serializer.serialize_map(Some(2))?;
159 map.serialize_entry("type", kind)?;
160 map.serialize_entry("value", &n)?;
161 map.end()
162}
163
164impl Serialize for Value {
165 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
166 match self {
167 Value::Number(n) => serialize_tagged_number("number", *n, serializer),
168 Value::Date(n) => serialize_tagged_number("date", *n, serializer),
169 Value::Zoned(z) => {
170 let mut map = serializer.serialize_map(Some(2))?;
171 map.serialize_entry("type", "zoned")?;
172 map.serialize_entry("value", &z.to_rfc9557())?;
173 map.end()
174 }
175 Value::Text(s) => {
176 let mut map = serializer.serialize_map(Some(2))?;
177 map.serialize_entry("type", "text")?;
178 map.serialize_entry("value", s)?;
179 map.end()
180 }
181 Value::Boolean(b) => {
182 let mut map = serializer.serialize_map(Some(2))?;
183 map.serialize_entry("type", "boolean")?;
184 map.serialize_entry("value", b)?;
185 map.end()
186 }
187 Value::Error(code) | Value::ErrorMsg(code, _) => {
191 let mut map = serializer.serialize_map(Some(2))?;
194 map.serialize_entry("error", code)?;
195 map.serialize_entry("type", "error")?;
196 map.end()
197 }
198 Value::Empty => {
199 let mut map = serializer.serialize_map(Some(2))?;
200 map.serialize_entry("type", "empty")?;
201 map.serialize_entry("value", &())?;
202 map.end()
203 }
204 Value::Array(rows) => {
205 if rows.is_empty() || rows[0].is_empty() {
206 return Err(S::Error::custom("array value must be non-empty"));
207 }
208 if rows.len() == 1 && rows[0].len() == 1 {
209 return Err(S::Error::custom(
210 "a 1x1 array does not exist in serialized form; collapse it \
211 to its scalar element (schema spec §6)",
212 ));
213 }
214 let width = rows[0].len();
215 for row in rows {
216 if row.len() != width {
217 return Err(S::Error::custom("array value must be rectangular"));
218 }
219 for v in row {
220 if matches!(v, Value::Array(_)) {
221 return Err(S::Error::custom(
222 "array elements must be scalar values (no nested arrays)",
223 ));
224 }
225 }
226 }
227 let mut map = serializer.serialize_map(Some(2))?;
228 map.serialize_entry("type", "array")?;
229 map.serialize_entry("value", rows)?;
230 map.end()
231 }
232 }
233 }
234}
235
236impl<'de> Deserialize<'de> for Value {
237 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
238 let raw = serde_json::Value::deserialize(deserializer)?;
241 parse_value(&raw).map_err(D::Error::custom)
242 }
243}
244
245fn parse_value(raw: &serde_json::Value) -> Result<Value, String> {
246 let obj = match raw.as_object() {
247 Some(obj) => obj,
248 None => return Err("a cell value must be a JSON object".to_string()),
249 };
250 let kind = match obj.get("type").and_then(serde_json::Value::as_str) {
251 Some(kind) => kind,
252 None => return Err("a cell value requires a string \"type\" field".to_string()),
253 };
254 let payload_key = if kind == "error" { "error" } else { "value" };
255 let payload = match obj.get(payload_key) {
256 Some(payload) if obj.len() == 2 => payload,
257 _ => {
258 return Err(format!(
259 "a {kind} value must have exactly the fields \"type\" and \"{payload_key}\""
260 ));
261 }
262 };
263 match kind {
264 "number" => Ok(Value::Number(parse_finite_f64(payload, kind)?)),
265 "date" => Ok(Value::Date(parse_finite_f64(payload, kind)?)),
266 "zoned" => match payload.as_str() {
267 Some(s) => parse_rfc9557(s)
268 .map(|zi| Value::Zoned(Box::new(zi)))
269 .ok_or_else(|| format!("a zoned value must be a valid RFC-9557 string, got {s:?}")),
270 None => Err("a zoned value must be a JSON string".to_string()),
271 },
272 "text" => match payload.as_str() {
273 Some(s) => Ok(Value::Text(s.to_owned())),
274 None => Err("a text value must be a JSON string".to_string()),
275 },
276 "boolean" => match payload.as_bool() {
277 Some(b) => Ok(Value::Boolean(b)),
278 None => Err("a boolean value must be a JSON boolean".to_string()),
279 },
280 "error" => match payload.as_str() {
281 Some(code) => Ok(Value::Error(code.to_owned())),
282 None => Err("an error value must carry a string error code".to_string()),
283 },
284 "empty" => {
285 if payload.is_null() {
286 Ok(Value::Empty)
287 } else {
288 Err("an empty value must be JSON null".to_string())
289 }
290 }
291 "array" => parse_array(payload),
292 other => Err(format!("unknown value type {other:?}")),
293 }
294}
295
296fn parse_finite_f64(payload: &serde_json::Value, kind: &str) -> Result<f64, String> {
297 let n = match payload.as_f64() {
303 Some(n) => n,
304 None => return Err(format!("a {kind} value must be a JSON number")),
305 };
306 if !n.is_finite() {
307 return Err(format!(
308 "non-finite {kind} value is forbidden (schema spec §8)"
309 ));
310 }
311 Ok(if n == 0.0 { 0.0 } else { n })
313}
314
315fn parse_array(payload: &serde_json::Value) -> Result<Value, String> {
316 let raw_rows = match payload.as_array() {
317 Some(rows) => rows,
318 None => return Err("an array value must be a 2-D JSON array".to_string()),
319 };
320 if raw_rows.is_empty() {
321 return Err("array value must be non-empty".to_string());
322 }
323 let mut rows = Vec::with_capacity(raw_rows.len());
324 let mut width = None;
325 for raw_row in raw_rows {
326 let raw_row = match raw_row.as_array() {
327 Some(row) => row,
328 None => return Err("array rows must be JSON arrays".to_string()),
329 };
330 if raw_row.is_empty() {
331 return Err("array value must be non-empty".to_string());
332 }
333 match width {
334 None => width = Some(raw_row.len()),
335 Some(w) if w != raw_row.len() => {
336 return Err("array value must be rectangular".to_string());
337 }
338 Some(_) => {}
339 }
340 let mut row = Vec::with_capacity(raw_row.len());
341 for raw_elem in raw_row {
342 let elem = parse_value(raw_elem)?;
343 if matches!(elem, Value::Array(_)) {
344 return Err("array elements must be scalar values (no nested arrays)".to_string());
345 }
346 row.push(elem);
347 }
348 rows.push(row);
349 }
350 if rows.len() == 1 && rows[0].len() == 1 {
351 return Err(
352 "a 1x1 array does not exist in serialized form; it must be collapsed \
353 to its scalar element (schema spec §6)"
354 .to_string(),
355 );
356 }
357 Ok(Value::Array(rows))
358}