compatible_with/
lib.rs

1//! # Compatible
2//! Compatibility Layer for older data using serde  
3//! You just need to provide a `Current: From<Old>` implementation  
4//! And the rest is handled automatically  
5//! Keep in mind that this uses untagged enums so it comes with performance cost  
6pub use compatible_with_derive::CompatibleWith;
7use serde::*;
8
9/// This is the main type you will be using  
10/// It wraps your old and current type and provides a way to deserialize existing data that might  
11/// match either of the types  
12/// It will deserialize the old type into an deserialize impl for the old type and then convert it  
13/// to the new type  
14/// ```rust,ignore
15/// #[derive(Serialize, Deserialize)]
16/// pub struct Current;
17/// pub struct SomeType {
18///     my_var: Current,
19/// }
20/// ```
21
22/// The `Current` version of the struct is `CompatibleWith<Old>`
23pub trait CompatibleWith<Old> {
24    fn from_old(value: Old) -> Self;
25}
26
27/// The `Old` is `CompatibleTo<Current>` version of the struct
28pub trait CompatibleTo<Current> {
29    fn into_current(self) -> Current;
30}
31
32impl<Old, Current> CompatibleWith<Old> for Current
33where
34    Current: From<Old>,
35{
36    fn from_old(value: Old) -> Self {
37        value.into()
38    }
39}
40
41impl<Old, Current> CompatibleTo<Current> for Old
42where
43    Current: CompatibleWith<Old>,
44{
45    fn into_current(self) -> Current {
46        Current::from_old(self)
47    }
48}
49
50mod with {
51    use super::{Compatible, CompatibleWith};
52    use serde::Deserialize;
53
54    impl<Old, Current> Compatible<Old, Current> {
55        pub fn deserialize_with<'de, D>(deserializer: D) -> Result<Current, D::Error>
56        where
57            D: serde::de::Deserializer<'de>,
58            Self: Deserialize<'de>,
59            Current: CompatibleWith<Old>,
60        {
61            let compatible: Compatible<Old, Current> = Compatible::deserialize(deserializer)?;
62            Ok(compatible.into_current())
63        }
64    }
65}
66
67#[derive(PartialEq, PartialOrd, Ord, Eq, Debug, Hash, Clone, Copy)]
68pub struct Compatible<Old, Current>(Alt<Old, Current>);
69
70impl<Old, Current> Compatible<Old, Current>
71where
72    Current: CompatibleWith<Old>,
73{
74    pub fn into_current(self) -> Current {
75        match self.0 {
76            Alt::Old(old) => old.into_current(),
77            Alt::Current(current) => current,
78        }
79    }
80
81    pub fn make_current(mut self) -> Self {
82        if let Alt::Old(old) = self.0 {
83            self.0 = Alt::Current(old.into_current())
84        };
85        self
86    }
87}
88
89#[derive(Deserialize)]
90#[serde(untagged)]
91#[derive(PartialEq, PartialOrd, Ord, Eq, Debug, Hash, Clone, Copy)]
92pub enum Alt<Old, Current> {
93    Old(Old),
94    Current(Current),
95}
96
97impl<'de, Old, Current> serde::de::Deserialize<'de> for Compatible<Old, Current>
98where
99    Current: CompatibleWith<Old>,
100    Alt<Old, Current>: serde::de::Deserialize<'de>,
101{
102    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
103    where
104        D: serde::de::Deserializer<'de>,
105    {
106        let alt = Alt::deserialize(deserializer)?;
107
108        Ok(Compatible(alt).make_current())
109    }
110}
111
112impl<Old, Current> serde::ser::Serialize for Compatible<Old, Current>
113where
114    Old: Serialize,
115    Current: Serialize,
116{
117    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
118    where
119        S: serde::ser::Serializer,
120    {
121        match self.0 {
122            Alt::Old(ref old) => old.serialize(serializer),
123            Alt::Current(ref current) => current.serialize(serializer),
124        }
125    }
126}
127
128#[test]
129pub fn test_simple() {
130    use serde::*;
131    #[derive(Serialize, Deserialize)]
132    pub struct Old {
133        pub a: i32,
134    }
135
136    #[derive(Serialize, Deserialize)]
137    pub struct New {
138        pub a: String,
139        pub b: i32,
140    }
141
142    impl From<Old> for New {
143        fn from(old: Old) -> Self {
144            New {
145                a: old.a.to_string(),
146                b: 0,
147            }
148        }
149    }
150
151    let old = Old { a: 1 };
152    let old_serialized = serde_json::to_string(&old).unwrap();
153    let migrated: Compatible<Old, New> = serde_json::from_str(&old_serialized).unwrap();
154    let migrated = migrated.into_current();
155
156    assert_eq!(migrated.a, "1");
157    assert_eq!(migrated.b, 0);
158}
159
160#[test]
161pub fn test_complex() {
162    use serde::*;
163    #[derive(Serialize, Deserialize)]
164    pub struct Dir {
165        id: i64,
166        name: String,
167        path: String,
168    }
169
170    #[derive(Serialize, Deserialize)]
171    pub struct DirNode {
172        id: i64,
173        name: String,
174        path: String,
175        children: Vec<DirNode>,
176    }
177
178    #[derive(Serialize, Deserialize)]
179    pub struct Old {
180        pub dirs: Vec<Dir>,
181        pub root: Dir,
182    }
183
184    #[derive(Serialize, Deserialize)]
185    pub struct New {
186        pub dirs: Compatible<Vec<Dir>, DirNode>,
187    }
188
189    impl From<Dir> for DirNode {
190        fn from(old: Dir) -> Self {
191            DirNode {
192                id: old.id,
193                name: old.name,
194                path: old.path,
195                children: vec![],
196            }
197        }
198    }
199
200    impl From<Vec<Dir>> for DirNode {
201        fn from(old: Vec<Dir>) -> Self {
202            let mut root = DirNode {
203                id: 0,
204                name: "root".to_string(),
205                path: "/".to_string(),
206                children: vec![],
207            };
208            root.children.extend(old.into_iter().map(|d| d.into()));
209            root
210        }
211    }
212
213    let old = Old {
214        dirs: vec![
215            Dir {
216                id: 1,
217                name: "a".to_string(),
218                path: "/a".to_string(),
219            },
220            Dir {
221                id: 2,
222                name: "b".to_string(),
223                path: "/b".to_string(),
224            },
225        ],
226        root: Dir {
227            id: 0,
228            name: "root".to_string(),
229            path: "/".to_string(),
230        },
231    };
232
233    let old_serialized = serde_json::to_string(&old).unwrap();
234    let migrated: New = serde_json::from_str(&old_serialized).unwrap();
235    let migrated_serialized = serde_json::to_string(&migrated).unwrap();
236    assert_eq!(
237        migrated_serialized,
238        r#"{"dirs":{"id":0,"name":"root","path":"/","children":[{"id":1,"name":"a","path":"/a","children":[]},{"id":2,"name":"b","path":"/b","children":[]}]}}"#
239    );
240}
241
242#[test]
243pub fn test_with() {
244    use serde::*;
245
246    #[derive(Debug, Deserialize, PartialEq)]
247    pub struct MyType(String);
248
249    #[derive(Serialize)]
250    pub struct Old {
251        pub a: i32,
252    }
253
254    impl From<i32> for MyType {
255        fn from(value: i32) -> Self {
256            MyType(value.to_string())
257        }
258    }
259    // impl<T: ToString> From<T> for MyType {
260    //     fn from(value: T) -> Self {
261    //         MyType(value.to_string())
262    //     }
263    // }
264
265    #[derive(Deserialize)]
266    pub struct New {
267        #[serde(deserialize_with = "Compatible::<i32, MyType>::deserialize_with")]
268        pub a: MyType,
269    }
270
271    let old = Old { a: 1 };
272    let old_serialized = serde_json::to_string(&old).unwrap();
273    let migrated: New = serde_json::from_str(&old_serialized).unwrap();
274
275    assert_eq!(migrated.a, MyType("1".into()));
276}