platform_value/value_serialization/
mod.rs

1use crate::value_serialization::ser::Serializer;
2use crate::{Error, Value};
3use serde::Deserialize;
4use serde::Serialize;
5
6pub mod de;
7pub mod ser;
8
9/// Convert a `T` into `platform_value::Value` which is an enum that can represent
10/// data.
11///
12/// # Example
13///
14/// ```
15/// use serde::Serialize;
16/// use platform_value::platform_value;
17///
18/// use std::error::Error;
19///
20/// #[derive(Serialize)]
21/// struct User {
22///     fingerprint: String,
23///     location: String,
24/// }
25///
26/// fn compare_platform_values() -> Result<(), Box<dyn Error>> {
27///     let u = User {
28///         fingerprint: "0xF9BA143B95FF6D82".to_owned(),
29///         location: "Menlo Park, CA".to_owned(),
30///     };
31///
32///     // The type of `expected` is `serde_json::Value`
33///     let expected = platform_value!({
34///         "fingerprint": "0xF9BA143B95FF6D82",
35///         "location": "Menlo Park, CA",
36///     });
37///
38///     let v = platform_value::to_value(u).unwrap();
39///     assert_eq!(v, expected);
40///
41///     Ok(())
42/// }
43/// #
44/// # compare_platform_values().unwrap();
45/// ```
46///
47/// # Errors
48///
49/// This conversion can fail if `T`'s implementation of `Serialize` decides to
50/// fail, or if `T` contains a map with non-string keys.
51///
52/// ```
53/// use std::collections::BTreeMap;
54///
55/// // The keys in this map are vectors, not strings.
56/// let mut map = BTreeMap::new();
57/// map.insert(vec![32, 64], "x86");
58///
59/// println!("{}", platform_value::to_value(map).unwrap_err());
60/// ```
61pub fn to_value<T>(value: T) -> Result<Value, Error>
62where
63    T: Serialize,
64{
65    value.serialize(Serializer)
66}
67
68/// Interpret a `serde_json::Value` as an instance of type `T`.
69///
70/// # Example
71///
72/// ```
73/// use serde::Deserialize;
74/// use platform_value::platform_value;
75///
76/// #[derive(Deserialize, Debug)]
77/// struct User {
78///     fingerprint: String,
79///     location: String,
80/// }
81///
82/// // The type of `j` is `serde_json::Value`
83/// let j = platform_value!({
84///     "fingerprint": "0xF9BA143B95FF6D82",
85///     "location": "Menlo Park, CA"
86/// });
87///
88/// let u: User = platform_value::from_value(j).unwrap();
89/// println!("{:#?}", u);
90/// ```
91///
92/// # Errors
93///
94/// This conversion can fail if the structure of the Value does not match the
95/// structure expected by `T`, for example if `T` is a struct type but the Value
96/// contains something other than a JSON map. It can also fail if the structure
97/// is correct but `T`'s implementation of `Deserialize` decides that something
98/// is wrong with the data, for example required struct fields are missing from
99/// the JSON map or some number is too big to fit in the expected primitive
100/// type.
101pub fn from_value<'de, T>(value: Value) -> Result<T, Error>
102where
103    T: Deserialize<'de>,
104{
105    T::deserialize(de::Deserializer(value))
106}
107
108#[cfg(test)]
109mod tests {
110    use serde::{Deserialize, Serialize};
111    use std::collections::HashMap;
112
113    use super::*;
114
115    #[test]
116    fn yeet() {
117        #[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)]
118        struct Yeet {
119            arr: Vec<String>,
120            map: HashMap<String, char>,
121            number: i32,
122            //todo: manage static strings
123            //static_string: &'static str,
124        }
125
126        let mut hm = HashMap::new();
127        hm.insert("wow".to_owned(), 'a');
128        hm.insert("lol".to_owned(), 'd');
129
130        let yeet = Yeet {
131            arr: vec!["kek".to_owned(), "top".to_owned()],
132            map: hm,
133            number: 420,
134            //static_string: "pizza",
135        };
136
137        let platform_value = to_value(yeet.clone()).expect("please");
138        let yeet_back: Yeet = from_value(platform_value).expect("please once again");
139
140        assert_eq!(yeet, yeet_back);
141    }
142
143    #[test]
144    fn test_externally_tagged_unit_variant() {
145        #[derive(Serialize, Deserialize, Debug, PartialEq)]
146        #[serde(rename_all = "camelCase")]
147        enum Choice {
148            Abstain,
149            Lock,
150            TowardsIdentity(String),
151        }
152
153        let v = to_value(&Choice::Abstain).unwrap();
154        assert_eq!(v, Value::Text("abstain".to_string()));
155        let back: Choice = from_value(v).unwrap();
156        assert_eq!(back, Choice::Abstain);
157
158        let v = to_value(&Choice::Lock).unwrap();
159        assert_eq!(v, Value::Text("lock".to_string()));
160        let back: Choice = from_value(v).unwrap();
161        assert_eq!(back, Choice::Lock);
162    }
163
164    #[test]
165    fn test_externally_tagged_newtype_variant() {
166        #[derive(Serialize, Deserialize, Debug, PartialEq)]
167        #[serde(rename_all = "camelCase")]
168        enum Choice {
169            Abstain,
170            Lock,
171            TowardsIdentity(String),
172        }
173
174        let v = to_value(&Choice::TowardsIdentity("abc".into())).unwrap();
175        let back: Choice = from_value(v).unwrap();
176        assert_eq!(back, Choice::TowardsIdentity("abc".into()));
177    }
178
179    #[test]
180    fn test_internally_tagged_enum() {
181        #[derive(Serialize, Deserialize, Debug, PartialEq)]
182        #[serde(tag = "$formatVersion")]
183        enum Info {
184            #[serde(rename = "0")]
185            V0 { name: String },
186        }
187
188        let v = to_value(&Info::V0 {
189            name: "test".into(),
190        })
191        .unwrap();
192        let back: Info = from_value(v).unwrap();
193        assert_eq!(
194            back,
195            Info::V0 {
196                name: "test".into()
197            }
198        );
199    }
200
201    #[test]
202    fn test_externally_tagged_struct_variant() {
203        #[derive(Serialize, Deserialize, Debug, PartialEq)]
204        enum Shape {
205            Circle { radius: f64 },
206            Rectangle { width: f64, height: f64 },
207        }
208
209        let v = to_value(&Shape::Circle { radius: 5.0 }).unwrap();
210        let back: Shape = from_value(v).unwrap();
211        assert_eq!(back, Shape::Circle { radius: 5.0 });
212
213        let v = to_value(&Shape::Rectangle {
214            width: 3.0,
215            height: 4.0,
216        })
217        .unwrap();
218        let back: Shape = from_value(v).unwrap();
219        assert_eq!(
220            back,
221            Shape::Rectangle {
222                width: 3.0,
223                height: 4.0
224            }
225        );
226    }
227
228    #[test]
229    fn test_externally_tagged_tuple_variant() {
230        #[derive(Serialize, Deserialize, Debug, PartialEq)]
231        enum Point {
232            TwoD(f64, f64),
233            ThreeD(f64, f64, f64),
234        }
235
236        let v = to_value(&Point::TwoD(1.0, 2.0)).unwrap();
237        let back: Point = from_value(v).unwrap();
238        assert_eq!(back, Point::TwoD(1.0, 2.0));
239
240        let v = to_value(&Point::ThreeD(1.0, 2.0, 3.0)).unwrap();
241        let back: Point = from_value(v).unwrap();
242        assert_eq!(back, Point::ThreeD(1.0, 2.0, 3.0));
243    }
244
245    #[test]
246    fn test_externally_tagged_newtype_wrapping_struct() {
247        #[derive(Serialize, Deserialize, Debug, PartialEq)]
248        #[serde(rename_all = "camelCase")]
249        enum Vote {
250            ResourceVote(InnerVote),
251        }
252
253        #[derive(Serialize, Deserialize, Debug, PartialEq)]
254        #[serde(rename_all = "camelCase")]
255        struct InnerVote {
256            poll_name: String,
257            choice: u32,
258        }
259
260        let v = to_value(&Vote::ResourceVote(InnerVote {
261            poll_name: "test".into(),
262            choice: 42,
263        }))
264        .unwrap();
265        let back: Vote = from_value(v).unwrap();
266        assert_eq!(
267            back,
268            Vote::ResourceVote(InnerVote {
269                poll_name: "test".into(),
270                choice: 42,
271            })
272        );
273    }
274}