ferriclink_core/
serializable.rs1use serde::{Deserialize, Serialize};
4use std::collections::HashMap;
5
6use crate::errors::{FerricLinkError, Result};
7
8pub trait Serializable: Serialize + for<'de> Deserialize<'de> + Send + Sync + 'static {
13 fn namespace() -> Vec<String>;
18
19 fn is_serializable() -> bool {
24 true
25 }
26
27 fn to_json(&self) -> Result<String> {
29 serde_json::to_string(self).map_err(FerricLinkError::from)
30 }
31
32 fn to_json_pretty(&self) -> Result<String> {
34 serde_json::to_string_pretty(self).map_err(FerricLinkError::from)
35 }
36
37 fn from_json(json: &str) -> Result<Self>
39 where
40 Self: Sized,
41 {
42 serde_json::from_str(json).map_err(FerricLinkError::from)
43 }
44
45 fn to_dict(&self) -> Result<HashMap<String, serde_json::Value>> {
47 let json = self.to_json()?;
48 serde_json::from_str(&json).map_err(FerricLinkError::from)
49 }
50
51 fn from_dict(dict: &HashMap<String, serde_json::Value>) -> Result<Self>
53 where
54 Self: Sized,
55 {
56 let json = serde_json::to_string(dict)?;
57 Self::from_json(&json)
58 }
59
60 fn type_name() -> &'static str {
64 std::any::type_name::<Self>()
65 }
66}
67
68#[macro_export]
73macro_rules! impl_serializable {
74 ($type:ty, $namespace:expr) => {
75 impl $crate::serializable::Serializable for $type {
76 fn namespace() -> Vec<String> {
77 $namespace.into_iter().map(|s| s.to_string()).collect()
78 }
79 }
80 };
81}
82
83pub trait Loadable: Serializable {
85 fn load(data: &[u8]) -> Result<Self>
87 where
88 Self: Sized,
89 {
90 let json = String::from_utf8(data.to_vec())
91 .map_err(|e| FerricLinkError::generic(format!("Invalid UTF-8: {e}")))?;
92 Self::from_json(&json)
93 }
94
95 fn save(&self) -> Result<Vec<u8>> {
97 let json = self.to_json()?;
98 Ok(json.into_bytes())
99 }
100}
101
102#[macro_export]
104macro_rules! impl_loadable {
105 ($type:ty) => {
106 impl $crate::serializable::Loadable for $type {}
107 };
108}
109
110#[cfg(test)]
111mod tests {
112 use super::*;
113 use serde::{Deserialize, Serialize};
114
115 #[derive(Serialize, Deserialize, Debug, PartialEq)]
116 struct TestMessage {
117 content: String,
118 message_type: String,
119 }
120
121 impl_serializable!(TestMessage, ["ferriclink", "messages", "test"]);
122 impl_loadable!(TestMessage);
123
124 #[test]
125 fn test_serialization() {
126 let msg = TestMessage {
127 content: "Hello, world!".to_string(),
128 message_type: "test".to_string(),
129 };
130
131 let json = msg.to_json().unwrap();
133 let deserialized: TestMessage = TestMessage::from_json(&json).unwrap();
134 assert_eq!(msg, deserialized);
135
136 let pretty_json = msg.to_json_pretty().unwrap();
138 assert!(pretty_json.contains("Hello, world!"));
139
140 let dict = msg.to_dict().unwrap();
142 let from_dict: TestMessage = TestMessage::from_dict(&dict).unwrap();
143 assert_eq!(msg, from_dict);
144 }
145
146 #[test]
147 fn test_namespace() {
148 let namespace = TestMessage::namespace();
149 assert_eq!(namespace, vec!["ferriclink", "messages", "test"]);
150 }
151
152 #[test]
153 fn test_type_name() {
154 let type_name = TestMessage::type_name();
155 assert!(type_name.contains("TestMessage"));
156 }
157
158 #[test]
159 fn test_loadable() {
160 let msg = TestMessage {
161 content: "Test content".to_string(),
162 message_type: "test".to_string(),
163 };
164
165 let data = msg.save().unwrap();
166 let loaded: TestMessage = TestMessage::load(&data).unwrap();
167 assert_eq!(msg, loaded);
168 }
169}