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 }
107}
108
109#[cfg(test)]
110mod tests {
111 use super::*;
112 use serde_json::json;
113
114 #[test]
115 fn test_json_data_dto_roundtrip() {
116 let original = json!({
117 "name": "test",
118 "count": 42,
119 "active": true,
120 "items": [1, 2, 3],
121 "nested": {
122 "value": null
123 }
124 });
125
126 let dto = JsonDataDto::from(original.clone());
127 let domain: JsonData = dto.clone().into();
128 let back: JsonDataDto = domain.into();
129
130 assert_eq!(dto.as_value(), back.as_value());
131 }
132
133 #[test]
134 fn test_null_conversion() {
135 let dto = JsonDataDto::from(SerdeValue::Null);
136 let domain: JsonData = dto.into();
137 assert!(matches!(domain, JsonData::Null));
138 }
139
140 #[test]
141 fn test_number_conversion() {
142 let int_dto = JsonDataDto::from(json!(42));
143 let int_domain: JsonData = int_dto.into();
144 assert!(matches!(int_domain, JsonData::Integer(42)));
145
146 let float_dto = JsonDataDto::from(json!(2.5));
147 let float_domain: JsonData = float_dto.into();
148 if let JsonData::Float(f) = float_domain {
149 assert!((f - 2.5).abs() < 0.001);
150 } else {
151 panic!("Expected Float");
152 }
153 }
154
155 #[test]
156 fn test_serde_serialization() {
157 let dto = JsonDataDto::from(json!({"key": "value"}));
158 let serialized = serde_json::to_string(&dto).unwrap();
159 let deserialized: JsonDataDto = serde_json::from_str(&serialized).unwrap();
160 assert_eq!(dto, deserialized);
161 }
162
163 #[test]
164 fn test_nan_infinity_conversion() {
165 let nan_domain = JsonData::Float(f64::NAN);
167 let nan_dto: JsonDataDto = nan_domain.into();
168 assert_eq!(nan_dto.as_value(), &SerdeValue::Null);
169
170 let inf_domain = JsonData::Float(f64::INFINITY);
172 let inf_dto: JsonDataDto = inf_domain.into();
173 assert_eq!(inf_dto.as_value(), &SerdeValue::Null);
174
175 let neg_inf_domain = JsonData::Float(f64::NEG_INFINITY);
177 let neg_inf_dto: JsonDataDto = neg_inf_domain.into();
178 assert_eq!(neg_inf_dto.as_value(), &SerdeValue::Null);
179 }
180
181 #[test]
182 fn test_empty_collections() {
183 let empty_array_dto = JsonDataDto::from(json!([]));
185 let empty_array_domain: JsonData = empty_array_dto.clone().into();
186 let back: JsonDataDto = empty_array_domain.into();
187 assert_eq!(empty_array_dto.as_value(), back.as_value());
188 assert!(matches!(
189 JsonData::from(empty_array_dto),
190 JsonData::Array(arr) if arr.is_empty()
191 ));
192
193 let empty_obj_dto = JsonDataDto::from(json!({}));
195 let empty_obj_domain: JsonData = empty_obj_dto.clone().into();
196 let back: JsonDataDto = empty_obj_domain.into();
197 assert_eq!(empty_obj_dto.as_value(), back.as_value());
198 assert!(matches!(
199 JsonData::from(empty_obj_dto),
200 JsonData::Object(obj) if obj.is_empty()
201 ));
202 }
203
204 #[test]
205 fn test_reference_conversion() {
206 let dto = JsonDataDto::from(json!({"ref_test": "value"}));
207
208 let domain_from_ref: JsonData = (&dto).into();
210 let domain_from_owned: JsonData = dto.clone().into();
211
212 assert_eq!(
214 format!("{:?}", domain_from_ref),
215 format!("{:?}", domain_from_owned)
216 );
217 }
218
219 #[test]
220 fn test_number_boundaries() {
221 let max_i64 = i64::MAX;
223 let max_dto = JsonDataDto::from(json!(max_i64));
224 let max_domain: JsonData = max_dto.into();
225 assert!(matches!(max_domain, JsonData::Integer(i) if i == max_i64));
226
227 let min_i64 = i64::MIN;
229 let min_dto = JsonDataDto::from(json!(min_i64));
230 let min_domain: JsonData = min_dto.into();
231 assert!(matches!(min_domain, JsonData::Integer(i) if i == min_i64));
232
233 let large_u64 = u64::MAX;
235 let large_dto = JsonDataDto::from(json!(large_u64));
236 let large_domain: JsonData = large_dto.into();
237 assert!(matches!(large_domain, JsonData::Float(_)));
239 }
240
241 #[test]
242 fn test_unicode_strings() {
243 let unicode_dto = JsonDataDto::from(json!("Hello, 世界"));
245 let unicode_domain: JsonData = unicode_dto.clone().into();
246 let back: JsonDataDto = unicode_domain.into();
247 assert_eq!(unicode_dto.as_value(), back.as_value());
248
249 let emoji_dto = JsonDataDto::from(json!("🦀 Rust"));
251 let emoji_domain: JsonData = emoji_dto.clone().into();
252 let back: JsonDataDto = emoji_domain.into();
253 assert_eq!(emoji_dto.as_value(), back.as_value());
254
255 let special_dto = JsonDataDto::from(json!("tab:\t newline:\n quote:\" backslash:\\"));
257 let special_domain: JsonData = special_dto.clone().into();
258 let back: JsonDataDto = special_domain.into();
259 assert_eq!(special_dto.as_value(), back.as_value());
260 }
261}