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
129impl Serializable for () {
130    fn serialize(&self) -> Option<String> {
131        "null".to_string().into()
132    }
133}
134
135impl<T: Serializable> Serializable for Vec<T> {
136    fn serialize(&self) -> Option<String> {
137        let serialized_elements: Vec<String> = self.iter().filter_map(|e| e.serialize()).collect();
138        format!("[{}]", serialized_elements.join(",")).into()
139    }
140}
141
142impl<T: Serializable> Serializable for Option<T> {
143    fn serialize(&self) -> Option<String> {
144        self.as_ref().and_then(|value| value.serialize())
145    }
146}
147
148impl<T: Serializable> Serializable for HashMap<String, T> {
149    fn serialize(&self) -> Option<String> {
150        self.iter()
151            .fold(Serializer::builder(), |mut builder, (key, value)| {
152                builder.set(key, value);
153                builder
154            })
155            .build()
156            .into()
157    }
158}
159
160impl<T: Serializable> Serializable for Box<T> {
161    fn serialize(&self) -> Option<String> {
162        self.as_ref().serialize()
163    }
164
165    fn set_metadata(&mut self, metadata: MetadataSchema) {
166        self.as_mut().set_metadata(metadata)
167    }
168
169    fn set_nullable(&mut self, nullable: bool) {
170        self.as_mut().set_nullable(nullable)
171    }
172
173    fn set_rename(&mut self, new_name: &str) {
174        self.as_mut().set_rename(new_name)
175    }
176}
177
178impl Serializable for Box<dyn Serializable> {
179    fn serialize(&self) -> Option<String> {
180        self.as_ref().serialize()
181    }
182
183    fn set_metadata(&mut self, metadata: MetadataSchema) {
184        self.as_mut().set_metadata(metadata)
185    }
186
187    fn set_nullable(&mut self, nullable: bool) {
188        self.as_mut().set_nullable(nullable)
189    }
190
191    fn set_rename(&mut self, new_name: &str) {
192        self.as_mut().set_rename(new_name)
193    }
194}
195
196#[cfg(feature = "chrono")]
197impl Serializable for chrono::DateTime<chrono::FixedOffset> {
198    fn serialize(&self) -> Option<String> {
199        self.to_rfc3339_opts(chrono::SecondsFormat::Millis, true)
200            .serialize()
201    }
202}
203
204#[cfg(feature = "chrono")]
205impl Serializable for chrono::DateTime<chrono::Utc> {
206    fn serialize(&self) -> Option<String> {
207        self.to_rfc3339_opts(chrono::SecondsFormat::Millis, true)
208            .serialize()
209    }
210}
211
212// TODO: implement other features serialization and general object serialization
213
214#[cfg(test)]
215mod tests {
216    use super::*;
217
218    #[derive(Clone)]
219    struct MockSerializable {
220        value: String,
221    }
222
223    impl Serializable for MockSerializable {
224        fn serialize(&self) -> Option<String> {
225            self.value.serialize()
226        }
227    }
228
229    #[test]
230    fn test_serialize() {
231        let mock = MockSerializable {
232            value: "test_value".to_string(),
233        };
234        assert_eq!(mock.serialize(), Some("\"test_value\"".to_string()));
235    }
236
237    #[test]
238    fn test_debug_trait() {
239        let mock = MockSerializable {
240            value: "debug_value".to_string(),
241        };
242        assert_eq!(
243            format!("{:?}", &mock as &dyn Serializable),
244            "\"debug_value\""
245        );
246    }
247
248    #[test]
249    fn test_partial_eq_trait() {
250        let mock1 = MockSerializable {
251            value: "value".to_string(),
252        };
253        let mock2 = MockSerializable {
254            value: "value".to_string(),
255        };
256        assert_eq!(&mock1 as &dyn Serializable, &mock2 as &dyn Serializable);
257    }
258
259    #[test]
260    fn test_serialize_str() {
261        let value = "Hello, world!";
262        assert_eq!(value.serialize(), Some("\"Hello, world!\"".to_string()));
263    }
264
265    #[test]
266    fn test_serialize_string() {
267        let value = "Hello, world!".to_string();
268        assert_eq!(value.serialize(), Some("\"Hello, world!\"".to_string()));
269    }
270
271    #[test]
272    fn test_serialize_vec() {
273        let vec = vec![
274            MockSerializable {
275                value: "value1".to_string(),
276            },
277            MockSerializable {
278                value: "value2".to_string(),
279            },
280        ];
281        let serialized: serde_json::Value =
282            serde_json::from_str(&vec.serialize().unwrap()).unwrap();
283
284        assert_eq!(serialized, serde_json::json!(["value1", "value2"]));
285    }
286
287    #[test]
288    fn test_serialize_option() {
289        let value = Some(MockSerializable {
290            value: "optional_value".to_string(),
291        });
292
293        let none_value: Option<MockSerializable> = None;
294
295        assert_eq!(value.serialize(), Some("\"optional_value\"".to_string()));
296        assert_eq!(none_value.serialize(), None);
297    }
298
299    #[test]
300    fn test_recursive_serialize_vec() {
301        let vec = vec![
302            MockSerializable {
303                value: "value1".to_string(),
304            },
305            MockSerializable {
306                value: "value2".to_string(),
307            },
308        ];
309
310        let serialized: serde_json::Value =
311            serde_json::from_str(&vec.serialize().unwrap()).unwrap();
312
313        assert_eq!(serialized, serde_json::json!(["value1", "value2"]));
314    }
315
316    #[test]
317    fn test_serialize_hashmap_basic() {
318        let mut hashmap: HashMap<String, String> = HashMap::new();
319        hashmap.insert("key1".to_string(), "value1".to_string());
320        hashmap.insert("key2".to_string(), "value2".to_string());
321
322        let serialized: serde_json::Value =
323            serde_json::from_str(&hashmap.serialize().unwrap()).unwrap();
324
325        assert_eq!(
326            serialized,
327            serde_json::json!({
328                "key1": "value1",
329                "key2": "value2"
330            })
331        );
332    }
333
334    #[test]
335    fn test_serialize_hashmap_partial_recursion() {
336        let hashmap = HashMap::from([
337            (
338                "value1".to_string(),
339                MockSerializable {
340                    value: "nested_value1".to_string(),
341                },
342            ),
343            (
344                "value2".to_string(),
345                MockSerializable {
346                    value: "nested_value2".to_string(),
347                },
348            ),
349        ]);
350
351        let serialized: serde_json::Value =
352            serde_json::from_str(&hashmap.serialize().unwrap()).unwrap();
353
354        assert_eq!(
355            serialized,
356            serde_json::json!({
357                "value1": "nested_value1",
358                "value2": "nested_value2"
359            })
360        );
361    }
362
363    #[test]
364    fn test_serialize_hashmap_recursion() {
365        let mut hashmap: HashMap<String, HashMap<String, MockSerializable>> = HashMap::new();
366        hashmap.insert(
367            "key1".to_string(),
368            HashMap::from([(
369                "value1".to_string(),
370                MockSerializable {
371                    value: "nested_value1".to_string(),
372                },
373            )]),
374        );
375        hashmap.insert(
376            "key2".to_string(),
377            HashMap::from([(
378                "value2".to_string(),
379                MockSerializable {
380                    value: "nested_value2".to_string(),
381                },
382            )]),
383        );
384
385        let serialized: serde_json::Value =
386            serde_json::from_str(&hashmap.serialize().unwrap()).unwrap();
387
388        assert_eq!(
389            serialized,
390            serde_json::json!({
391                "key1": {
392                    "value1": "nested_value1"
393                },
394                "key2": {
395                    "value2": "nested_value2"
396                }
397            })
398        );
399    }
400
401    #[test]
402    fn test_serialize_box() {
403        let boxed_value = Box::new(MockSerializable {
404            value: "boxed_value".to_string(),
405        });
406        assert_eq!(boxed_value.serialize(), Some("\"boxed_value\"".to_string()));
407    }
408
409    #[test]
410    fn test_serialize_box_dyn() {
411        let boxed_value: Box<dyn Serializable> = Box::new(MockSerializable {
412            value: "boxed_dyn_value".to_string(),
413        });
414        assert_eq!(
415            boxed_value.serialize(),
416            Some("\"boxed_dyn_value\"".to_string())
417        );
418    }
419
420    #[cfg(feature = "chrono")]
421    #[test]
422    fn test_serialize_chrono_fixed_offset() {
423        use chrono::{DateTime, FixedOffset};
424
425        let datetime =
426            DateTime::<FixedOffset>::parse_from_rfc3339("1985-04-12T23:20:50.520Z").unwrap();
427
428        assert_eq!(
429            datetime.serialize(),
430            Some("\"1985-04-12T23:20:50.520Z\"".to_string())
431        );
432    }
433
434    #[cfg(feature = "chrono")]
435    #[test]
436    fn test_serialize_chrono_utc() {
437        use chrono::{DateTime, FixedOffset, Utc};
438        let datetime =
439            DateTime::<FixedOffset>::parse_from_rfc3339("1985-04-12T23:20:50.520Z").unwrap();
440        let datetime_utc = datetime.with_timezone(&Utc);
441
442        assert_eq!(
443            datetime_utc.serialize(),
444            Some("\"1985-04-12T23:20:50.520Z\"".to_string())
445        );
446    }
447}