1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
//! # Compatible
//! Compatibility Layer for older data using serde  
//! You just need to provide a `Current: From<Old>` implementation  
//! And the rest is handled automatically  
//! Keep in mind that this uses untagged enums so it comes with performance cost  
pub use compatible_with_derive::CompatibleWith;
use serde::*;

/// This is the main type you will be using  
/// It wraps your old and current type and provides a way to deserialize existing data that might  
/// match either of the types  
/// It will deserialize the old type into an deserialize impl for the old type and then convert it  
/// to the new type  
/// ```rust,ignore
/// #[derive(Serialize, Deserialize)]
/// pub struct Current;
/// pub struct SomeType {
///     my_var: Current,
/// }
/// ```

/// The `Current` version of the struct is `CompatibleWith<Old>`
pub trait CompatibleWith<Old> {
    fn from_old(value: Old) -> Self;
}

/// The `Old` is `CompatibleTo<Current>` version of the struct
pub trait CompatibleTo<Current> {
    fn into_current(self) -> Current;
}

impl<Old, Current> CompatibleWith<Old> for Current
where
    Current: From<Old>,
{
    fn from_old(value: Old) -> Self {
        value.into()
    }
}

impl<Old, Current> CompatibleTo<Current> for Old
where
    Current: CompatibleWith<Old>,
{
    fn into_current(self) -> Current {
        Current::from_old(self)
    }
}

#[derive(PartialEq, PartialOrd, Ord, Eq, Debug, Hash, Clone, Copy)]
pub struct Compatible<Old, Current>(Alt<Old, Current>);

impl<Old, Current> Compatible<Old, Current>
where
    Current: CompatibleWith<Old>,
{
    pub fn into_current(self) -> Current {
        match self.0 {
            Alt::Old(old) => old.into_current(),
            Alt::Current(current) => current,
        }
    }

    pub fn make_current(mut self) -> Self {
        if let Alt::Old(old) = self.0 {
            self.0 = Alt::Current(old.into_current())
        };
        self
    }
}

#[derive(Deserialize)]
#[serde(untagged)]
#[derive(PartialEq, PartialOrd, Ord, Eq, Debug, Hash, Clone, Copy)]
pub enum Alt<Old, Current> {
    Old(Old),
    Current(Current),
}

impl<'de, Old, Current> serde::de::Deserialize<'de> for Compatible<Old, Current>
where
    Current: CompatibleWith<Old>,
    Alt<Old, Current>: serde::de::Deserialize<'de>,
{
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: serde::de::Deserializer<'de>,
    {
        let alt = Alt::deserialize(deserializer)?;

        Ok(Compatible(alt).make_current())
    }
}

impl<Old, Current> serde::ser::Serialize for Compatible<Old, Current>
where
    Old: Serialize,
    Current: Serialize,
{
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: serde::ser::Serializer,
    {
        match self.0 {
            Alt::Old(ref old) => old.serialize(serializer),
            Alt::Current(ref current) => current.serialize(serializer),
        }
    }
}

#[test]
pub fn test_simple() {
    use serde::*;
    #[derive(Serialize, Deserialize)]
    pub struct Old {
        pub a: i32,
    }

    #[derive(Serialize, Deserialize)]
    pub struct New {
        pub a: String,
        pub b: i32,
    }

    impl From<Old> for New {
        fn from(old: Old) -> Self {
            New {
                a: old.a.to_string(),
                b: 0,
            }
        }
    }

    let old = Old { a: 1 };
    let old_serialized = serde_json::to_string(&old).unwrap();
    let migrated: Compatible<Old, New> = serde_json::from_str(&old_serialized).unwrap();
    let migrated = migrated.into_current();

    assert_eq!(migrated.a, "1");
    assert_eq!(migrated.b, 0);
}

#[test]
pub fn test_complex() {
    use serde::*;
    #[derive(Serialize, Deserialize)]
    pub struct Dir {
        id: i64,
        name: String,
        path: String,
    }

    #[derive(Serialize, Deserialize)]
    pub struct DirNode {
        id: i64,
        name: String,
        path: String,
        children: Vec<DirNode>,
    }

    #[derive(Serialize, Deserialize)]
    pub struct Old {
        pub dirs: Vec<Dir>,
        pub root: Dir,
    }

    #[derive(Serialize, Deserialize)]
    pub struct New {
        pub dirs: Compatible<Vec<Dir>, DirNode>,
    }

    impl From<Dir> for DirNode {
        fn from(old: Dir) -> Self {
            DirNode {
                id: old.id,
                name: old.name,
                path: old.path,
                children: vec![],
            }
        }
    }

    impl From<Vec<Dir>> for DirNode {
        fn from(old: Vec<Dir>) -> Self {
            let mut root = DirNode {
                id: 0,
                name: "root".to_string(),
                path: "/".to_string(),
                children: vec![],
            };
            root.children.extend(old.into_iter().map(|d| d.into()));
            root
        }
    }

    let old = Old {
        dirs: vec![
            Dir {
                id: 1,
                name: "a".to_string(),
                path: "/a".to_string(),
            },
            Dir {
                id: 2,
                name: "b".to_string(),
                path: "/b".to_string(),
            },
        ],
        root: Dir {
            id: 0,
            name: "root".to_string(),
            path: "/".to_string(),
        },
    };

    let old_serialized = serde_json::to_string(&old).unwrap();
    let migrated: New = serde_json::from_str(&old_serialized).unwrap();
    let migrated_serialized = serde_json::to_string(&migrated).unwrap();
    assert_eq!(
        migrated_serialized,
        r#"{"dirs":{"id":0,"name":"root","path":"/","children":[{"id":1,"name":"a","path":"/a","children":[]},{"id":2,"name":"b","path":"/b","children":[]}]}}"#
    );
}