1pub use compatible_with_derive::CompatibleWith;
7use serde::*;
8
9pub trait CompatibleWith<Old> {
24 fn from_old(value: Old) -> Self;
25}
26
27pub 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 #[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}