arri_repr/
serializable.rs

1// TODO: create a macro which automatically generates this implementation with a derive
2use std::{any::type_name, collections::HashMap, fmt::Debug};
3
4use downcast_rs::{Downcast, impl_downcast};
5
6use crate::{MetadataSchema, serializer::Serializer};
7
8/// Triggers a panic with a detailed error message, including the type, serialized data,
9/// and value that caused the issue. This function is intended to report bugs.
10///
11/// # Parameters
12/// - `message`: A message describing the context of the panic.
13/// - `serialized`: The serialized representation of the data.
14/// - `value`: The original value that caused the issue.
15///
16/// # Panics
17/// This function always panics with a formatted message containing the provided details.
18///
19/// # Note
20/// The panic message includes a link to report bugs, encouraging users to provide feedback.
21fn do_panic<T>(message: impl std::fmt::Display, serialized: impl Debug, value: impl Debug) -> !
22where
23    T: ?Sized,
24{
25    panic!(
26        "{}!\n\
27        This is a bug, please report it @ <https://github.com/Arthurdw/ronky/issues>\n\
28        Type: {:?}\n\
29        Serialized: {:?}\n\
30        Value: {:?}",
31        message,
32        type_name::<T>(),
33        serialized,
34        value
35    );
36}
37
38/// A trait for types that can be serialized into a string representation.
39///
40/// This trait also provides default implementations for setting metadata,
41/// nullability, and renaming, which trigger a panic if not implemented.
42pub trait Serializable: Downcast {
43    /// Serializes the object into an optional string representation.
44    ///
45    /// # Returns
46    /// An `Option<String>` containing the serialized representation, or `None` if serialization fails.
47    fn serialize(&self) -> Option<String>;
48
49    /// Sets metadata for the object.
50    ///
51    /// # Arguments
52    /// - `metadata`: The metadata to set.
53    ///
54    /// # Panics
55    /// This method panics if not implemented for the type.
56    fn set_metadata(&mut self, metadata: MetadataSchema) {
57        do_panic::<Self>(
58            "set_metadata is not implemented for this type",
59            self.serialize(),
60            metadata,
61        );
62    }
63
64    /// Sets the nullability of the object.
65    ///
66    /// # Arguments
67    /// - `nullable`: A boolean indicating whether the object is nullable.
68    ///
69    /// # Panics
70    /// This method panics if not implemented for the type.
71    fn set_nullable(&mut self, nullable: bool) {
72        do_panic::<Self>(
73            "set_nullable is not implemented for this type",
74            self.serialize(),
75            nullable,
76        );
77    }
78
79    /// Renames the object.
80    ///
81    /// # Arguments
82    /// - `new_name`: The new name to assign to the object.
83    ///
84    /// # Panics
85    /// This method panics if not implemented for the type.
86    fn set_rename(&mut self, new_name: &str) {
87        do_panic::<Self>(
88            "set_rename is not implemented for this type",
89            self.serialize(),
90            new_name,
91        );
92    }
93}
94
95impl_downcast!(Serializable);
96
97impl Debug for dyn Serializable {
98    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
99        write!(f, "{}", self.serialize().unwrap_or("undefined".to_string()))
100    }
101}
102
103impl PartialEq for dyn Serializable {
104    fn eq(&self, other: &Self) -> bool {
105        self.serialize() == other.serialize()
106    }
107}
108
109impl Eq for dyn Serializable {}
110
111impl Serializable for &'static str {
112    fn serialize(&self) -> Option<String> {
113        format!("\"{}\"", self).into()
114    }
115}
116
117impl Serializable for String {
118    fn serialize(&self) -> Option<String> {
119        format!("\"{}\"", self).into()
120    }
121}
122
123impl Serializable for bool {
124    fn serialize(&self) -> Option<String> {
125        self.to_string().into()
126    }
127}
128
129// Macro to generate Serializable implementations for numeric types
130macro_rules! impl_serializable_for_numeric {
131    ($($type:ty),* $(,)?) => {
132        $(
133            impl Serializable for $type {
134                fn serialize(&self) -> Option<String> {
135                    self.to_string().into()
136                }
137            }
138        )*
139    };
140}
141
142// Numeric type implementations
143impl_serializable_for_numeric!(
144    i8, i16, i32, i64, i128, isize, u8, u16, u32, u64, u128, usize, f32, f64,
145);
146
147impl Serializable for () {
148    fn serialize(&self) -> Option<String> {
149        "null".to_string().into()
150    }
151}
152
153impl<T: Serializable> Serializable for Vec<T> {
154    fn serialize(&self) -> Option<String> {
155        let serialized_elements: Vec<String> = self.iter().filter_map(|e| e.serialize()).collect();
156        format!("[{}]", serialized_elements.join(",")).into()
157    }
158}
159
160impl<T: Serializable> Serializable for Option<T> {
161    fn serialize(&self) -> Option<String> {
162        self.as_ref().and_then(|value| value.serialize())
163    }
164}
165
166impl<T: Serializable> Serializable for HashMap<String, T> {
167    fn serialize(&self) -> Option<String> {
168        self.iter()
169            .fold(Serializer::builder(), |mut builder, (key, value)| {
170                builder.set(key, value);
171                builder
172            })
173            .build()
174            .into()
175    }
176}
177
178impl<T: Serializable> Serializable for indexmap::IndexMap<String, T> {
179    fn serialize(&self) -> Option<String> {
180        self.iter()
181            .fold(Serializer::builder(), |mut builder, (key, value)| {
182                builder.set(key, value);
183                builder
184            })
185            .build()
186            .into()
187    }
188}
189
190impl<T: Serializable> Serializable for Box<T> {
191    fn serialize(&self) -> Option<String> {
192        self.as_ref().serialize()
193    }
194
195    fn set_metadata(&mut self, metadata: MetadataSchema) {
196        self.as_mut().set_metadata(metadata)
197    }
198
199    fn set_nullable(&mut self, nullable: bool) {
200        self.as_mut().set_nullable(nullable)
201    }
202
203    fn set_rename(&mut self, new_name: &str) {
204        self.as_mut().set_rename(new_name)
205    }
206}
207
208impl Serializable for Box<dyn Serializable> {
209    fn serialize(&self) -> Option<String> {
210        self.as_ref().serialize()
211    }
212
213    fn set_metadata(&mut self, metadata: MetadataSchema) {
214        self.as_mut().set_metadata(metadata)
215    }
216
217    fn set_nullable(&mut self, nullable: bool) {
218        self.as_mut().set_nullable(nullable)
219    }
220
221    fn set_rename(&mut self, new_name: &str) {
222        self.as_mut().set_rename(new_name)
223    }
224}
225
226#[cfg(feature = "chrono")]
227impl Serializable for chrono::DateTime<chrono::FixedOffset> {
228    fn serialize(&self) -> Option<String> {
229        self.to_rfc3339_opts(chrono::SecondsFormat::Millis, true)
230            .serialize()
231    }
232}
233
234#[cfg(feature = "chrono")]
235impl Serializable for chrono::DateTime<chrono::Utc> {
236    fn serialize(&self) -> Option<String> {
237        self.to_rfc3339_opts(chrono::SecondsFormat::Millis, true)
238            .serialize()
239    }
240}
241
242// TODO: implement other features serialization and general object serialization
243
244#[cfg(test)]
245mod tests {
246    use super::*;
247
248    #[derive(Clone)]
249    struct MockSerializable {
250        value: String,
251    }
252
253    impl Serializable for MockSerializable {
254        fn serialize(&self) -> Option<String> {
255            self.value.serialize()
256        }
257    }
258
259    #[test]
260    fn test_serialize() {
261        let mock = MockSerializable {
262            value: "test_value".to_string(),
263        };
264        assert_eq!(mock.serialize(), Some("\"test_value\"".to_string()));
265    }
266
267    #[test]
268    fn test_debug_trait() {
269        let mock = MockSerializable {
270            value: "debug_value".to_string(),
271        };
272        assert_eq!(
273            format!("{:?}", &mock as &dyn Serializable),
274            "\"debug_value\""
275        );
276    }
277
278    #[test]
279    fn test_partial_eq_trait() {
280        let mock1 = MockSerializable {
281            value: "value".to_string(),
282        };
283        let mock2 = MockSerializable {
284            value: "value".to_string(),
285        };
286        assert_eq!(&mock1 as &dyn Serializable, &mock2 as &dyn Serializable);
287    }
288
289    #[test]
290    fn test_serialize_str() {
291        let value = "Hello, world!";
292        assert_eq!(value.serialize(), Some("\"Hello, world!\"".to_string()));
293    }
294
295    #[test]
296    fn test_serialize_string() {
297        let value = "Hello, world!".to_string();
298        assert_eq!(value.serialize(), Some("\"Hello, world!\"".to_string()));
299    }
300
301    #[test]
302    fn test_serialize_vec() {
303        let vec = vec![
304            MockSerializable {
305                value: "value1".to_string(),
306            },
307            MockSerializable {
308                value: "value2".to_string(),
309            },
310        ];
311        let serialized: serde_json::Value =
312            serde_json::from_str(&vec.serialize().unwrap()).unwrap();
313
314        assert_eq!(serialized, serde_json::json!(["value1", "value2"]));
315    }
316
317    #[test]
318    fn test_serialize_option() {
319        let value = Some(MockSerializable {
320            value: "optional_value".to_string(),
321        });
322
323        let none_value: Option<MockSerializable> = None;
324
325        assert_eq!(value.serialize(), Some("\"optional_value\"".to_string()));
326        assert_eq!(none_value.serialize(), None);
327    }
328
329    #[test]
330    fn test_recursive_serialize_vec() {
331        let vec = vec![
332            MockSerializable {
333                value: "value1".to_string(),
334            },
335            MockSerializable {
336                value: "value2".to_string(),
337            },
338        ];
339
340        let serialized: serde_json::Value =
341            serde_json::from_str(&vec.serialize().unwrap()).unwrap();
342
343        assert_eq!(serialized, serde_json::json!(["value1", "value2"]));
344    }
345
346    #[test]
347    fn test_serialize_hashmap_basic() {
348        let mut hashmap: HashMap<String, String> = HashMap::new();
349        hashmap.insert("key1".to_string(), "value1".to_string());
350        hashmap.insert("key2".to_string(), "value2".to_string());
351
352        let serialized: serde_json::Value =
353            serde_json::from_str(&hashmap.serialize().unwrap()).unwrap();
354
355        assert_eq!(
356            serialized,
357            serde_json::json!({
358                "key1": "value1",
359                "key2": "value2"
360            })
361        );
362    }
363
364    #[test]
365    fn test_serialize_hashmap_partial_recursion() {
366        let hashmap = HashMap::from([
367            (
368                "value1".to_string(),
369                MockSerializable {
370                    value: "nested_value1".to_string(),
371                },
372            ),
373            (
374                "value2".to_string(),
375                MockSerializable {
376                    value: "nested_value2".to_string(),
377                },
378            ),
379        ]);
380
381        let serialized: serde_json::Value =
382            serde_json::from_str(&hashmap.serialize().unwrap()).unwrap();
383
384        assert_eq!(
385            serialized,
386            serde_json::json!({
387                "value1": "nested_value1",
388                "value2": "nested_value2"
389            })
390        );
391    }
392
393    #[test]
394    fn test_serialize_hashmap_recursion() {
395        let mut hashmap: HashMap<String, HashMap<String, MockSerializable>> = HashMap::new();
396        hashmap.insert(
397            "key1".to_string(),
398            HashMap::from([(
399                "value1".to_string(),
400                MockSerializable {
401                    value: "nested_value1".to_string(),
402                },
403            )]),
404        );
405        hashmap.insert(
406            "key2".to_string(),
407            HashMap::from([(
408                "value2".to_string(),
409                MockSerializable {
410                    value: "nested_value2".to_string(),
411                },
412            )]),
413        );
414
415        let serialized: serde_json::Value =
416            serde_json::from_str(&hashmap.serialize().unwrap()).unwrap();
417
418        assert_eq!(
419            serialized,
420            serde_json::json!({
421                "key1": {
422                    "value1": "nested_value1"
423                },
424                "key2": {
425                    "value2": "nested_value2"
426                }
427            })
428        );
429    }
430
431    #[test]
432    fn test_serialize_box() {
433        let boxed_value = Box::new(MockSerializable {
434            value: "boxed_value".to_string(),
435        });
436        assert_eq!(boxed_value.serialize(), Some("\"boxed_value\"".to_string()));
437    }
438
439    #[test]
440    fn test_serialize_box_dyn() {
441        let boxed_value: Box<dyn Serializable> = Box::new(MockSerializable {
442            value: "boxed_dyn_value".to_string(),
443        });
444        assert_eq!(
445            boxed_value.serialize(),
446            Some("\"boxed_dyn_value\"".to_string())
447        );
448    }
449
450    #[cfg(feature = "chrono")]
451    #[test]
452    fn test_serialize_chrono_fixed_offset() {
453        use chrono::{DateTime, FixedOffset};
454
455        let datetime =
456            DateTime::<FixedOffset>::parse_from_rfc3339("1985-04-12T23:20:50.520Z").unwrap();
457
458        assert_eq!(
459            datetime.serialize(),
460            Some("\"1985-04-12T23:20:50.520Z\"".to_string())
461        );
462    }
463
464    #[cfg(feature = "chrono")]
465    #[test]
466    fn test_serialize_chrono_utc() {
467        use chrono::{DateTime, FixedOffset, Utc};
468        let datetime =
469            DateTime::<FixedOffset>::parse_from_rfc3339("1985-04-12T23:20:50.520Z").unwrap();
470        let datetime_utc = datetime.with_timezone(&Utc);
471
472        assert_eq!(
473            datetime_utc.serialize(),
474            Some("\"1985-04-12T23:20:50.520Z\"".to_string())
475        );
476    }
477}