1use std::{any::type_name, collections::HashMap, fmt::Debug};
3
4use downcast_rs::{Downcast, impl_downcast};
5
6use crate::{MetadataSchema, serializer::Serializer};
7
8fn escape_json_string(s: &str) -> String {
21 let mut result = String::with_capacity(s.len() + 16);
22 result.push('"');
23
24 for ch in s.chars() {
25 match ch {
26 '"' => result.push_str("\\\""),
27 '\\' => result.push_str("\\\\"),
28 '/' => result.push_str("\\/"),
29 '\u{0008}' => result.push_str("\\b"), '\u{000C}' => result.push_str("\\f"), '\n' => result.push_str("\\n"),
32 '\r' => result.push_str("\\r"),
33 '\t' => result.push_str("\\t"),
34 ch if ch.is_control() => {
35 result.push_str(&format!("\\u{:04x}", ch as u32));
37 }
38 ch => result.push(ch),
39 }
40 }
41
42 result.push('"');
43 result
44}
45
46fn do_panic<T>(message: impl std::fmt::Display, serialized: impl Debug, value: impl Debug) -> !
60where
61 T: ?Sized,
62{
63 panic!(
64 "{}!\n\
65 This is a bug, please report it @ <https://github.com/Arthurdw/ronky/issues>\n\
66 Type: {:?}\n\
67 Serialized: {:?}\n\
68 Value: {:?}",
69 message,
70 type_name::<T>(),
71 serialized,
72 value
73 );
74}
75
76pub trait Serializable: Downcast {
81 fn serialize(&self) -> Option<String>;
86
87 fn set_metadata(&mut self, metadata: MetadataSchema) {
95 do_panic::<Self>(
96 "set_metadata is not implemented for this type",
97 self.serialize(),
98 metadata,
99 );
100 }
101
102 fn set_nullable(&mut self, nullable: bool) {
110 do_panic::<Self>(
111 "set_nullable is not implemented for this type",
112 self.serialize(),
113 nullable,
114 );
115 }
116
117 fn set_rename(&mut self, new_name: &str) {
125 do_panic::<Self>(
126 "set_rename is not implemented for this type",
127 self.serialize(),
128 new_name,
129 );
130 }
131}
132
133impl_downcast!(Serializable);
134
135impl Debug for dyn Serializable {
136 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
137 write!(f, "{}", self.serialize().unwrap_or("undefined".to_string()))
138 }
139}
140
141impl PartialEq for dyn Serializable {
142 fn eq(&self, other: &Self) -> bool {
143 self.serialize() == other.serialize()
144 }
145}
146
147impl Eq for dyn Serializable {}
148
149impl Serializable for &'static str {
150 fn serialize(&self) -> Option<String> {
151 escape_json_string(self).into()
152 }
153}
154
155impl Serializable for String {
156 fn serialize(&self) -> Option<String> {
157 escape_json_string(self).into()
158 }
159}
160
161impl Serializable for bool {
162 fn serialize(&self) -> Option<String> {
163 self.to_string().into()
164 }
165}
166
167macro_rules! impl_serializable_for_numeric {
169 ($($type:ty),* $(,)?) => {
170 $(
171 impl Serializable for $type {
172 fn serialize(&self) -> Option<String> {
173 self.to_string().into()
174 }
175 }
176 )*
177 };
178}
179
180impl_serializable_for_numeric!(
182 i8, i16, i32, i64, i128, isize, u8, u16, u32, u64, u128, usize, f32, f64,
183);
184
185impl Serializable for () {
186 fn serialize(&self) -> Option<String> {
187 "null".to_string().into()
188 }
189}
190
191impl<T: Serializable> Serializable for Vec<T> {
192 fn serialize(&self) -> Option<String> {
193 let serialized_elements: Vec<String> = self.iter().filter_map(|e| e.serialize()).collect();
194 format!("[{}]", serialized_elements.join(",")).into()
195 }
196}
197
198impl<T: Serializable> Serializable for Option<T> {
199 fn serialize(&self) -> Option<String> {
200 self.as_ref().and_then(|value| value.serialize())
201 }
202}
203
204impl<T: Serializable> Serializable for HashMap<String, T> {
205 fn serialize(&self) -> Option<String> {
206 self.iter()
207 .fold(Serializer::builder(), |mut builder, (key, value)| {
208 builder.set(key, value);
209 builder
210 })
211 .build()
212 .into()
213 }
214}
215
216impl<T: Serializable> Serializable for indexmap::IndexMap<String, T> {
217 fn serialize(&self) -> Option<String> {
218 self.iter()
219 .fold(Serializer::builder(), |mut builder, (key, value)| {
220 builder.set(key, value);
221 builder
222 })
223 .build()
224 .into()
225 }
226}
227
228impl<T: Serializable> Serializable for Box<T> {
229 fn serialize(&self) -> Option<String> {
230 self.as_ref().serialize()
231 }
232
233 fn set_metadata(&mut self, metadata: MetadataSchema) {
234 self.as_mut().set_metadata(metadata)
235 }
236
237 fn set_nullable(&mut self, nullable: bool) {
238 self.as_mut().set_nullable(nullable)
239 }
240
241 fn set_rename(&mut self, new_name: &str) {
242 self.as_mut().set_rename(new_name)
243 }
244}
245
246impl Serializable for Box<dyn Serializable> {
247 fn serialize(&self) -> Option<String> {
248 self.as_ref().serialize()
249 }
250
251 fn set_metadata(&mut self, metadata: MetadataSchema) {
252 self.as_mut().set_metadata(metadata)
253 }
254
255 fn set_nullable(&mut self, nullable: bool) {
256 self.as_mut().set_nullable(nullable)
257 }
258
259 fn set_rename(&mut self, new_name: &str) {
260 self.as_mut().set_rename(new_name)
261 }
262}
263
264#[cfg(feature = "chrono")]
265impl Serializable for chrono::DateTime<chrono::FixedOffset> {
266 fn serialize(&self) -> Option<String> {
267 self.to_rfc3339_opts(chrono::SecondsFormat::Millis, true)
268 .serialize()
269 }
270}
271
272#[cfg(feature = "chrono")]
273impl Serializable for chrono::DateTime<chrono::Utc> {
274 fn serialize(&self) -> Option<String> {
275 self.to_rfc3339_opts(chrono::SecondsFormat::Millis, true)
276 .serialize()
277 }
278}
279
280#[cfg(test)]
283mod tests {
284 use super::*;
285
286 #[derive(Clone)]
287 struct MockSerializable {
288 value: String,
289 }
290
291 impl Serializable for MockSerializable {
292 fn serialize(&self) -> Option<String> {
293 self.value.serialize()
294 }
295 }
296
297 #[test]
298 fn test_serialize() {
299 let mock = MockSerializable {
300 value: "test_value".to_string(),
301 };
302 assert_eq!(mock.serialize(), Some("\"test_value\"".to_string()));
303 }
304
305 #[test]
306 fn test_debug_trait() {
307 let mock = MockSerializable {
308 value: "debug_value".to_string(),
309 };
310 assert_eq!(
311 format!("{:?}", &mock as &dyn Serializable),
312 "\"debug_value\""
313 );
314 }
315
316 #[test]
317 fn test_partial_eq_trait() {
318 let mock1 = MockSerializable {
319 value: "value".to_string(),
320 };
321 let mock2 = MockSerializable {
322 value: "value".to_string(),
323 };
324 assert_eq!(&mock1 as &dyn Serializable, &mock2 as &dyn Serializable);
325 }
326
327 #[test]
328 fn test_serialize_str() {
329 let value = "Hello, world!";
330 assert_eq!(value.serialize(), Some("\"Hello, world!\"".to_string()));
331 }
332
333 #[test]
334 fn test_serialize_string() {
335 let value = "Hello, world!".to_string();
336 assert_eq!(value.serialize(), Some("\"Hello, world!\"".to_string()));
337 }
338
339 #[test]
340 fn test_serialize_vec() {
341 let vec = vec![
342 MockSerializable {
343 value: "value1".to_string(),
344 },
345 MockSerializable {
346 value: "value2".to_string(),
347 },
348 ];
349 let serialized: serde_json::Value =
350 serde_json::from_str(&vec.serialize().unwrap()).unwrap();
351
352 assert_eq!(serialized, serde_json::json!(["value1", "value2"]));
353 }
354
355 #[test]
356 fn test_serialize_option() {
357 let value = Some(MockSerializable {
358 value: "optional_value".to_string(),
359 });
360
361 let none_value: Option<MockSerializable> = None;
362
363 assert_eq!(value.serialize(), Some("\"optional_value\"".to_string()));
364 assert_eq!(none_value.serialize(), None);
365 }
366
367 #[test]
368 fn test_recursive_serialize_vec() {
369 let vec = vec![
370 MockSerializable {
371 value: "value1".to_string(),
372 },
373 MockSerializable {
374 value: "value2".to_string(),
375 },
376 ];
377
378 let serialized: serde_json::Value =
379 serde_json::from_str(&vec.serialize().unwrap()).unwrap();
380
381 assert_eq!(serialized, serde_json::json!(["value1", "value2"]));
382 }
383
384 #[test]
385 fn test_serialize_hashmap_basic() {
386 let mut hashmap: HashMap<String, String> = HashMap::new();
387 hashmap.insert("key1".to_string(), "value1".to_string());
388 hashmap.insert("key2".to_string(), "value2".to_string());
389
390 let serialized: serde_json::Value =
391 serde_json::from_str(&hashmap.serialize().unwrap()).unwrap();
392
393 assert_eq!(
394 serialized,
395 serde_json::json!({
396 "key1": "value1",
397 "key2": "value2"
398 })
399 );
400 }
401
402 #[test]
403 fn test_serialize_hashmap_partial_recursion() {
404 let hashmap = HashMap::from([
405 (
406 "value1".to_string(),
407 MockSerializable {
408 value: "nested_value1".to_string(),
409 },
410 ),
411 (
412 "value2".to_string(),
413 MockSerializable {
414 value: "nested_value2".to_string(),
415 },
416 ),
417 ]);
418
419 let serialized: serde_json::Value =
420 serde_json::from_str(&hashmap.serialize().unwrap()).unwrap();
421
422 assert_eq!(
423 serialized,
424 serde_json::json!({
425 "value1": "nested_value1",
426 "value2": "nested_value2"
427 })
428 );
429 }
430
431 #[test]
432 fn test_serialize_hashmap_recursion() {
433 let mut hashmap: HashMap<String, HashMap<String, MockSerializable>> = HashMap::new();
434 hashmap.insert(
435 "key1".to_string(),
436 HashMap::from([(
437 "value1".to_string(),
438 MockSerializable {
439 value: "nested_value1".to_string(),
440 },
441 )]),
442 );
443 hashmap.insert(
444 "key2".to_string(),
445 HashMap::from([(
446 "value2".to_string(),
447 MockSerializable {
448 value: "nested_value2".to_string(),
449 },
450 )]),
451 );
452
453 let serialized: serde_json::Value =
454 serde_json::from_str(&hashmap.serialize().unwrap()).unwrap();
455
456 assert_eq!(
457 serialized,
458 serde_json::json!({
459 "key1": {
460 "value1": "nested_value1"
461 },
462 "key2": {
463 "value2": "nested_value2"
464 }
465 })
466 );
467 }
468
469 #[test]
470 fn test_serialize_box() {
471 let boxed_value = Box::new(MockSerializable {
472 value: "boxed_value".to_string(),
473 });
474 assert_eq!(boxed_value.serialize(), Some("\"boxed_value\"".to_string()));
475 }
476
477 #[test]
478 fn test_serialize_box_dyn() {
479 let boxed_value: Box<dyn Serializable> = Box::new(MockSerializable {
480 value: "boxed_dyn_value".to_string(),
481 });
482 assert_eq!(
483 boxed_value.serialize(),
484 Some("\"boxed_dyn_value\"".to_string())
485 );
486 }
487
488 #[cfg(feature = "chrono")]
489 #[test]
490 fn test_serialize_chrono_fixed_offset() {
491 use chrono::{DateTime, FixedOffset};
492
493 let datetime =
494 DateTime::<FixedOffset>::parse_from_rfc3339("1985-04-12T23:20:50.520Z").unwrap();
495
496 assert_eq!(
497 datetime.serialize(),
498 Some("\"1985-04-12T23:20:50.520Z\"".to_string())
499 );
500 }
501
502 #[cfg(feature = "chrono")]
503 #[test]
504 fn test_serialize_chrono_utc() {
505 use chrono::{DateTime, FixedOffset, Utc};
506 let datetime =
507 DateTime::<FixedOffset>::parse_from_rfc3339("1985-04-12T23:20:50.520Z").unwrap();
508 let datetime_utc = datetime.with_timezone(&Utc);
509
510 assert_eq!(
511 datetime_utc.serialize(),
512 Some("\"1985-04-12T23:20:50.520Z\"".to_string())
513 );
514 }
515
516 #[test]
517 fn test_serialize_string_with_special_characters() {
518 let test_cases: Vec<(&str, &str)> = vec![
520 ("simple", "\"simple\""),
521 ("with\"quote", "\"with\\\"quote\""),
522 ("with\\backslash", "\"with\\\\backslash\""),
523 ("with\nnewline", "\"with\\nnewline\""),
524 ("with\ttab", "\"with\\ttab\""),
525 ("with\rcarriage", "\"with\\rcarriage\""),
526 ("with/solidus", "\"with\\/solidus\""),
527 (
528 "a comma separated list of tags to filter by\nex: \"foo,bar\"",
529 "\"a comma separated list of tags to filter by\\nex: \\\"foo,bar\\\"\"",
530 ),
531 ];
532
533 for (input, expected) in test_cases {
534 let serialized = input.to_string().serialize().unwrap();
535 let result: serde_json::Value = serde_json::from_str(&serialized)
536 .unwrap_or_else(|_| panic!("Failed to parse JSON for input: {}", input));
537 let expected_result: serde_json::Value = serde_json::from_str(expected)
538 .unwrap_or_else(|_| panic!("Failed to parse expected JSON: {}", expected));
539 assert_eq!(result, expected_result, "Mismatch for input: {}", input);
540 }
541 }
542}