pjson_rs/application/dto/
json_data_dto.rs1use crate::domain::value_objects::JsonData;
14use serde::{Deserialize, Serialize};
15use serde_json::Value as SerdeValue;
16
17#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
22#[serde(transparent)]
23pub struct JsonDataDto {
24 value: SerdeValue,
25}
26
27impl JsonDataDto {
28 pub fn new(value: SerdeValue) -> Self {
30 Self { value }
31 }
32
33 pub fn into_inner(self) -> SerdeValue {
35 self.value
36 }
37
38 pub fn as_value(&self) -> &SerdeValue {
40 &self.value
41 }
42}
43
44impl From<SerdeValue> for JsonDataDto {
45 fn from(value: SerdeValue) -> Self {
46 Self { value }
47 }
48}
49
50impl From<JsonDataDto> for SerdeValue {
51 fn from(dto: JsonDataDto) -> Self {
52 dto.value
53 }
54}
55
56impl From<JsonDataDto> for JsonData {
57 fn from(dto: JsonDataDto) -> Self {
58 JsonData::from(dto.value)
60 }
61}
62
63impl From<&JsonDataDto> for JsonData {
64 fn from(dto: &JsonDataDto) -> Self {
65 JsonData::from(dto.value.clone())
66 }
67}
68
69impl From<JsonData> for JsonDataDto {
70 fn from(data: JsonData) -> Self {
71 Self {
72 value: convert_domain_to_serde(&data),
73 }
74 }
75}
76
77fn convert_domain_to_serde(data: &JsonData) -> SerdeValue {
88 match data {
89 JsonData::Null => SerdeValue::Null,
90 JsonData::Bool(b) => SerdeValue::Bool(*b),
91 JsonData::Integer(i) => SerdeValue::Number((*i).into()),
92 JsonData::Float(f) => serde_json::Number::from_f64(*f)
93 .map(SerdeValue::Number)
94 .unwrap_or(SerdeValue::Null),
95 JsonData::String(s) => SerdeValue::String(s.clone()),
96 JsonData::Array(arr) => {
97 SerdeValue::Array(arr.iter().map(convert_domain_to_serde).collect())
98 }
99 JsonData::Object(map) => {
100 let obj: serde_json::Map<String, SerdeValue> = map
101 .iter()
102 .map(|(k, v)| (k.clone(), convert_domain_to_serde(v)))
103 .collect();
104 SerdeValue::Object(obj)
105 }
106 _ => SerdeValue::Null,
107 }
108}
109
110#[cfg(test)]
111mod tests {
112 use super::*;
113 use serde_json::json;
114
115 #[test]
116 fn test_json_data_dto_roundtrip() {
117 let original = json!({
118 "name": "test",
119 "count": 42,
120 "active": true,
121 "items": [1, 2, 3],
122 "nested": {
123 "value": null
124 }
125 });
126
127 let dto = JsonDataDto::from(original.clone());
128 let domain: JsonData = dto.clone().into();
129 let back: JsonDataDto = domain.into();
130
131 assert_eq!(dto.as_value(), back.as_value());
132 }
133
134 #[test]
135 fn test_null_conversion() {
136 let dto = JsonDataDto::from(SerdeValue::Null);
137 let domain: JsonData = dto.into();
138 assert!(matches!(domain, JsonData::Null));
139 }
140
141 #[test]
142 fn test_number_conversion() {
143 let int_dto = JsonDataDto::from(json!(42));
144 let int_domain: JsonData = int_dto.into();
145 assert!(matches!(int_domain, JsonData::Integer(42)));
146
147 let float_dto = JsonDataDto::from(json!(2.5));
148 let float_domain: JsonData = float_dto.into();
149 if let JsonData::Float(f) = float_domain {
150 assert!((f - 2.5).abs() < 0.001);
151 } else {
152 panic!("Expected Float");
153 }
154 }
155
156 #[test]
157 fn test_serde_serialization() {
158 let dto = JsonDataDto::from(json!({"key": "value"}));
159 let serialized = serde_json::to_string(&dto).unwrap();
160 let deserialized: JsonDataDto = serde_json::from_str(&serialized).unwrap();
161 assert_eq!(dto, deserialized);
162 }
163
164 #[test]
165 fn test_nan_infinity_conversion() {
166 let nan_domain = JsonData::Float(f64::NAN);
168 let nan_dto: JsonDataDto = nan_domain.into();
169 assert_eq!(nan_dto.as_value(), &SerdeValue::Null);
170
171 let inf_domain = JsonData::Float(f64::INFINITY);
173 let inf_dto: JsonDataDto = inf_domain.into();
174 assert_eq!(inf_dto.as_value(), &SerdeValue::Null);
175
176 let neg_inf_domain = JsonData::Float(f64::NEG_INFINITY);
178 let neg_inf_dto: JsonDataDto = neg_inf_domain.into();
179 assert_eq!(neg_inf_dto.as_value(), &SerdeValue::Null);
180 }
181
182 #[test]
183 fn test_empty_collections() {
184 let empty_array_dto = JsonDataDto::from(json!([]));
186 let empty_array_domain: JsonData = empty_array_dto.clone().into();
187 let back: JsonDataDto = empty_array_domain.into();
188 assert_eq!(empty_array_dto.as_value(), back.as_value());
189 assert!(matches!(
190 JsonData::from(empty_array_dto),
191 JsonData::Array(arr) if arr.is_empty()
192 ));
193
194 let empty_obj_dto = JsonDataDto::from(json!({}));
196 let empty_obj_domain: JsonData = empty_obj_dto.clone().into();
197 let back: JsonDataDto = empty_obj_domain.into();
198 assert_eq!(empty_obj_dto.as_value(), back.as_value());
199 assert!(matches!(
200 JsonData::from(empty_obj_dto),
201 JsonData::Object(obj) if obj.is_empty()
202 ));
203 }
204
205 #[test]
206 fn test_reference_conversion() {
207 let dto = JsonDataDto::from(json!({"ref_test": "value"}));
208
209 let domain_from_ref: JsonData = (&dto).into();
211 let domain_from_owned: JsonData = dto.clone().into();
212
213 assert_eq!(
215 format!("{:?}", domain_from_ref),
216 format!("{:?}", domain_from_owned)
217 );
218 }
219
220 #[test]
221 fn test_number_boundaries() {
222 let max_i64 = i64::MAX;
224 let max_dto = JsonDataDto::from(json!(max_i64));
225 let max_domain: JsonData = max_dto.into();
226 assert!(matches!(max_domain, JsonData::Integer(i) if i == max_i64));
227
228 let min_i64 = i64::MIN;
230 let min_dto = JsonDataDto::from(json!(min_i64));
231 let min_domain: JsonData = min_dto.into();
232 assert!(matches!(min_domain, JsonData::Integer(i) if i == min_i64));
233
234 let large_u64 = u64::MAX;
236 let large_dto = JsonDataDto::from(json!(large_u64));
237 let large_domain: JsonData = large_dto.into();
238 assert!(matches!(large_domain, JsonData::Float(_)));
240 }
241
242 #[test]
243 fn test_unicode_strings() {
244 let unicode_dto = JsonDataDto::from(json!("Hello, 世界"));
246 let unicode_domain: JsonData = unicode_dto.clone().into();
247 let back: JsonDataDto = unicode_domain.into();
248 assert_eq!(unicode_dto.as_value(), back.as_value());
249
250 let emoji_dto = JsonDataDto::from(json!("🦀 Rust"));
252 let emoji_domain: JsonData = emoji_dto.clone().into();
253 let back: JsonDataDto = emoji_domain.into();
254 assert_eq!(emoji_dto.as_value(), back.as_value());
255
256 let special_dto = JsonDataDto::from(json!("tab:\t newline:\n quote:\" backslash:\\"));
258 let special_domain: JsonData = special_dto.clone().into();
259 let back: JsonDataDto = special_domain.into();
260 assert_eq!(special_dto.as_value(), back.as_value());
261 }
262}