pjson_rs_domain/value_objects/
json_data.rs1use crate::{DomainError, DomainResult};
7use serde::{Deserialize, Serialize};
8use std::collections::HashMap;
9use std::fmt;
10
11#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
14#[non_exhaustive]
15pub enum JsonData {
16 #[default]
17 Null,
19 Bool(bool),
21 Integer(i64),
23 Float(f64),
25 String(String),
27 Array(Vec<JsonData>),
29 Object(HashMap<String, JsonData>),
31}
32
33impl JsonData {
34 pub fn null() -> Self {
36 Self::Null
37 }
38
39 pub fn bool(value: bool) -> Self {
41 Self::Bool(value)
42 }
43
44 pub fn integer(value: i64) -> Self {
46 Self::Integer(value)
47 }
48
49 pub fn float(value: f64) -> DomainResult<Self> {
65 if value.is_nan() || value.is_infinite() {
66 return Err(DomainError::InvalidInput(
67 "JSON does not support NaN or infinite float values (RFC 8259 §6)".to_string(),
68 ));
69 }
70 Ok(Self::Float(value))
71 }
72
73 pub fn string<S: Into<String>>(value: S) -> Self {
75 Self::String(value.into())
76 }
77
78 pub fn array(values: Vec<JsonData>) -> Self {
80 Self::Array(values)
81 }
82
83 pub fn object(values: HashMap<String, JsonData>) -> Self {
85 Self::Object(values)
86 }
87
88 pub fn is_null(&self) -> bool {
90 matches!(self, Self::Null)
91 }
92
93 pub fn is_bool(&self) -> bool {
95 matches!(self, Self::Bool(_))
96 }
97
98 pub fn is_integer(&self) -> bool {
100 matches!(self, Self::Integer(_))
101 }
102
103 pub fn is_float(&self) -> bool {
105 matches!(self, Self::Float(_))
106 }
107
108 pub fn is_number(&self) -> bool {
110 matches!(self, Self::Integer(_) | Self::Float(_))
111 }
112
113 pub fn is_string(&self) -> bool {
115 matches!(self, Self::String(_))
116 }
117
118 pub fn is_array(&self) -> bool {
120 matches!(self, Self::Array(_))
121 }
122
123 pub fn is_object(&self) -> bool {
125 matches!(self, Self::Object(_))
126 }
127
128 pub fn as_bool(&self) -> Option<bool> {
130 match self {
131 Self::Bool(b) => Some(*b),
132 _ => None,
133 }
134 }
135
136 pub fn as_i64(&self) -> Option<i64> {
138 match self {
139 Self::Integer(i) => Some(*i),
140 _ => None,
141 }
142 }
143
144 pub fn as_f64(&self) -> Option<f64> {
146 match self {
147 Self::Float(f) => Some(*f),
148 Self::Integer(i) => Some(*i as f64),
149 _ => None,
150 }
151 }
152
153 pub fn as_str(&self) -> Option<&str> {
155 match self {
156 Self::String(s) => Some(s),
157 _ => None,
158 }
159 }
160
161 pub fn as_array(&self) -> Option<&Vec<JsonData>> {
163 match self {
164 Self::Array(arr) => Some(arr),
165 _ => None,
166 }
167 }
168
169 pub fn as_array_mut(&mut self) -> Option<&mut Vec<JsonData>> {
171 match self {
172 Self::Array(arr) => Some(arr),
173 _ => None,
174 }
175 }
176
177 pub fn as_object(&self) -> Option<&HashMap<String, JsonData>> {
179 match self {
180 Self::Object(obj) => Some(obj),
181 _ => None,
182 }
183 }
184
185 pub fn as_object_mut(&mut self) -> Option<&mut HashMap<String, JsonData>> {
187 match self {
188 Self::Object(obj) => Some(obj),
189 _ => None,
190 }
191 }
192
193 pub fn get(&self, key: &str) -> Option<&JsonData> {
195 match self {
196 Self::Object(obj) => obj.get(key),
197 _ => None,
198 }
199 }
200
201 pub fn get_path(&self, path: &str) -> Option<&JsonData> {
203 let parts: Vec<&str> = path.split('.').collect();
204 let mut current = self;
205
206 for part in parts {
207 match current {
208 Self::Object(obj) => {
209 current = obj.get(part)?;
210 }
211 _ => return None,
212 }
213 }
214
215 Some(current)
216 }
217
218 pub fn set_path(&mut self, path: &str, value: JsonData) -> bool {
220 let parts: Vec<&str> = path.split('.').collect();
221 if parts.is_empty() {
222 return false;
223 }
224
225 if parts.len() == 1 {
226 if let Self::Object(obj) = self {
227 obj.insert(parts[0].to_string(), value);
228 return true;
229 }
230 return false;
231 }
232
233 let mut current = self;
235 for part in &parts[..parts.len() - 1] {
236 match current {
237 Self::Object(obj) => {
238 if !obj.contains_key(*part) {
239 obj.insert(part.to_string(), Self::object(HashMap::new()));
240 }
241 current = obj
242 .get_mut(*part)
243 .expect("Key must exist as we just inserted it above");
244 }
245 _ => return false,
246 }
247 }
248
249 if let Self::Object(obj) = current {
251 obj.insert(parts[parts.len() - 1].to_string(), value);
252 true
253 } else {
254 false
255 }
256 }
257
258 pub fn memory_size(&self) -> usize {
260 match self {
261 Self::Null => 1,
262 Self::Bool(_) => 1,
263 Self::Integer(_) => 8,
264 Self::Float(_) => 8,
265 Self::String(s) => s.len() * 2, Self::Array(arr) => 8 + arr.iter().map(|v| v.memory_size()).sum::<usize>(),
267 Self::Object(obj) => {
268 16 + obj
269 .iter()
270 .map(|(k, v)| k.len() * 2 + v.memory_size())
271 .sum::<usize>()
272 }
273 }
274 }
275}
276
277impl fmt::Display for JsonData {
278 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
279 match self {
280 Self::Null => write!(f, "null"),
281 Self::Bool(b) => write!(f, "{b}"),
282 Self::Integer(i) => write!(f, "{i}"),
283 Self::Float(float_val) => write!(f, "{float_val}"),
284 Self::String(s) => write!(f, "\"{s}\""),
285 Self::Array(arr) => {
286 write!(f, "[")?;
287 for (i, item) in arr.iter().enumerate() {
288 if i > 0 {
289 write!(f, ",")?;
290 }
291 write!(f, "{item}")?;
292 }
293 write!(f, "]")
294 }
295 Self::Object(obj) => {
296 write!(f, "{{")?;
297 for (i, (key, value)) in obj.iter().enumerate() {
298 if i > 0 {
299 write!(f, ",")?;
300 }
301 write!(f, "\"{key}\":{value}")?;
302 }
303 write!(f, "}}")
304 }
305 }
306 }
307}
308
309impl Eq for JsonData {}
310
311impl std::hash::Hash for JsonData {
312 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
313 match self {
314 Self::Null => 0u8.hash(state),
315 Self::Bool(b) => {
316 1u8.hash(state);
317 b.hash(state);
318 }
319 Self::Integer(i) => {
320 2u8.hash(state);
321 i.hash(state);
322 }
323 Self::Float(f) => {
324 3u8.hash(state);
325 f.to_bits().hash(state);
327 }
328 Self::String(s) => {
329 4u8.hash(state);
330 s.hash(state);
331 }
332 Self::Array(arr) => {
333 5u8.hash(state);
334 arr.hash(state);
335 }
336 Self::Object(obj) => {
337 6u8.hash(state);
338 let mut pairs: Vec<_> = obj.iter().collect();
341 pairs.sort_by_key(|(k, _)| *k);
342 pairs.hash(state);
343 }
344 }
345 }
346}
347
348impl From<bool> for JsonData {
349 fn from(value: bool) -> Self {
350 Self::Bool(value)
351 }
352}
353
354impl From<i64> for JsonData {
355 fn from(value: i64) -> Self {
356 Self::Integer(value)
357 }
358}
359
360impl From<String> for JsonData {
361 fn from(value: String) -> Self {
362 Self::String(value)
363 }
364}
365
366impl From<&str> for JsonData {
367 fn from(value: &str) -> Self {
368 Self::String(value.to_string())
369 }
370}
371
372impl From<Vec<JsonData>> for JsonData {
373 fn from(value: Vec<JsonData>) -> Self {
374 Self::Array(value)
375 }
376}
377
378impl From<HashMap<String, JsonData>> for JsonData {
379 fn from(value: HashMap<String, JsonData>) -> Self {
380 Self::Object(value)
381 }
382}
383
384impl From<serde_json::Value> for JsonData {
385 fn from(value: serde_json::Value) -> Self {
386 match value {
387 serde_json::Value::Null => Self::Null,
388 serde_json::Value::Bool(b) => Self::Bool(b),
389 serde_json::Value::Number(n) => {
390 if let Some(i) = n.as_i64() {
391 Self::Integer(i)
392 } else if let Some(f) = n.as_f64() {
393 Self::Float(f)
394 } else {
395 Self::Float(0.0) }
397 }
398 serde_json::Value::String(s) => Self::String(s),
399 serde_json::Value::Array(arr) => {
400 let converted: Vec<JsonData> = arr.into_iter().map(JsonData::from).collect();
401 Self::Array(converted)
402 }
403 serde_json::Value::Object(obj) => {
404 let converted: HashMap<String, JsonData> = obj
405 .into_iter()
406 .map(|(k, v)| (k, JsonData::from(v)))
407 .collect();
408 Self::Object(converted)
409 }
410 }
411 }
412}
413
414#[cfg(test)]
415mod tests {
416 use super::*;
417
418 #[test]
419 fn test_json_data_creation() {
420 assert_eq!(JsonData::null(), JsonData::Null);
421 assert_eq!(JsonData::bool(true), JsonData::Bool(true));
422 assert_eq!(JsonData::float(42.0).unwrap(), JsonData::Float(42.0));
423 assert_eq!(
424 JsonData::string("hello"),
425 JsonData::String("hello".to_string())
426 );
427 }
428
429 #[test]
430 fn test_json_data_type_checks() {
431 assert!(JsonData::null().is_null());
432 assert!(JsonData::bool(true).is_bool());
433 assert!(JsonData::float(42.0).unwrap().is_number());
434 assert!(JsonData::string("hello").is_string());
435 assert!(JsonData::array(vec![]).is_array());
436 assert!(JsonData::object(HashMap::new()).is_object());
437 }
438
439 #[test]
440 fn test_json_data_conversions() {
441 assert_eq!(JsonData::bool(true).as_bool(), Some(true));
442 assert_eq!(JsonData::float(42.0).unwrap().as_f64(), Some(42.0));
443 assert_eq!(JsonData::integer(42).as_i64(), Some(42));
444 assert_eq!(JsonData::string("hello").as_str(), Some("hello"));
445 }
446
447 #[test]
448 fn test_path_operations() {
449 let mut data = JsonData::object(HashMap::new());
450
451 assert!(data.set_path("user.name", JsonData::string("John")));
453 assert!(data.set_path("user.age", JsonData::integer(30)));
454
455 assert_eq!(data.get_path("user.name").unwrap().as_str(), Some("John"));
457 assert_eq!(data.get_path("user.age").unwrap().as_i64(), Some(30));
458
459 assert!(data.get_path("user.email").is_none());
461 }
462
463 #[test]
464 fn test_memory_size() {
465 let data = JsonData::object(
466 [
467 ("name".to_string(), JsonData::string("John")),
468 ("age".to_string(), JsonData::integer(30)),
469 ]
470 .into_iter()
471 .collect(),
472 );
473
474 assert!(data.memory_size() > 0);
475 }
476
477 #[test]
478 fn test_display() {
479 let data = JsonData::object(
480 [
481 ("name".to_string(), JsonData::string("John")),
482 ("active".to_string(), JsonData::bool(true)),
483 ]
484 .into_iter()
485 .collect(),
486 );
487
488 let display = format!("{data}");
489 assert!(display.contains("name"));
490 assert!(display.contains("John"));
491 }
492}