serde_firestore_value/
ser.rs

1mod firestore_array_value_serializer;
2mod firestore_geo_point_value_serializer;
3mod firestore_map_value_serializer;
4mod firestore_reference_value_serializer;
5mod firestore_timestamp_value_serializer;
6mod firestore_value_serializer;
7mod firestore_value_struct_serializer;
8mod name_map_value_serializer;
9pub(crate) mod with;
10
11use serde::Serialize;
12
13use crate::google::firestore::v1::Value;
14use crate::{ser::firestore_value_serializer::FirestoreValueSerializer, Error};
15
16pub use firestore_value_serializer::FirestoreValueSerializer as Serializer;
17
18/// Serialize an instance of type `T` to a Firestore Value.
19///
20/// # Examples
21///
22/// ```rust
23/// # fn main() -> anyhow::Result<()> {
24/// #     use serde_firestore_value::google::firestore::v1::{value::ValueType, MapValue, Value};
25/// #     use serde_firestore_value::to_value;
26/// #[derive(serde::Serialize)]
27/// struct T {
28///     b: bool,
29///     n: i64,
30/// }
31/// assert_eq!(
32///     to_value(&T { b: true, n: 1 })?,
33///     Value {
34///         value_type: Some(ValueType::MapValue(MapValue {
35///             fields: std::collections::HashMap::from([
36///                 (
37///                     "b".to_string(),
38///                     Value {
39///                         value_type: Some(ValueType::BooleanValue(true))
40///                     }
41///                 ),
42///                 (
43///                     "n".to_string(),
44///                     Value {
45///                         value_type: Some(ValueType::IntegerValue(1))
46///                     }
47///                 )
48///             ])
49///         }))
50///     }
51/// );
52/// #     Ok(())
53/// # }
54/// ```
55///
56/// # Serialize GeoPoint, Reference, and Timestamp
57///
58/// See: [`with`](crate::with).
59///
60/// # Mapping table
61///
62/// | [serde data model]         | [Firestore Value]                   |
63/// |----------------------------|-------------------------------------|
64/// | bool                       | booleanValue                        |
65/// | i8                         | integerValue                        |
66/// | i16                        | integerValue                        |
67/// | i32                        | integerValue                        |
68/// | i64                        | integerValue                        |
69/// | i128                       | (not supported)                     |
70/// | u8                         | integerValue                        |
71/// | u16                        | integerValue                        |
72/// | u32                        | integerValue                        |
73/// | u64                        | (not supported)                     |
74/// | u128                       | (not supported)                     |
75/// | f32                        | doubleValue                         |
76/// | f64                        | doubleValue                         |
77/// | char                       | stringValue                         |
78/// | string                     | stringValue                         |
79/// | byte array                 | bytesValue                          |
80/// | option                     | nullValue or (value)                |
81/// | unit                       | nullValue                           |
82/// | unit_struct                | nullValue                           |
83/// | unit_variant               | stringValue                         |
84/// | newtype_struct             | (value)                             |
85/// | newtype_struct (reference) | referenceValue                      |
86/// | newtype_variant            | mapValue (`{ (name): (value) }`)    |
87/// | seq                        | arrayValue                          |
88/// | tuple                      | arrayValue                          |
89/// | tuple_struct               | arrayValue                          |
90/// | tuple_variant              | mapValue (`{ (name): arrayValue }`) |
91/// | map                        | mapValue (`{ (key): (value) }`)     |
92/// | struct                     | mapValue (`{ (field): (value) }`)   |
93/// | struct (lat_lng)           | geoPointValue                       |
94/// | struct (timestamp)         | timestampValue                      |
95/// | struct_variant             | mapValue (`{ (name): mapValue }`)   |
96///
97/// [Firestore Value]: https://firebase.google.com/docs/firestore/reference/rest/v1/Value
98/// [serde data model]: https://serde.rs/data-model.html
99pub fn to_value<T>(value: &T) -> Result<Value, Error>
100where
101    T: Serialize,
102{
103    value.serialize(FirestoreValueSerializer)
104}
105
106#[cfg(test)]
107mod tests {
108    use std::collections::{BTreeMap, HashMap};
109
110    const MAX_BYTE_LEN: usize = 1_048_487;
111
112    use crate::value_ext::ValueExt;
113
114    use super::*;
115
116    #[test]
117    fn test_bool() -> anyhow::Result<()> {
118        assert_eq!(to_value(&false)?, Value::from_bool(false));
119        assert_eq!(to_value(&true)?, Value::from_bool(true));
120        Ok(())
121    }
122
123    #[test]
124    fn test_i8() -> anyhow::Result<()> {
125        assert_eq!(to_value(&i8::MAX)?, Value::from_i64(i64::from(i8::MAX)));
126        assert_eq!(to_value(&i8::MIN)?, Value::from_i64(i64::from(i8::MIN)));
127        Ok(())
128    }
129
130    #[test]
131    fn test_i16() -> anyhow::Result<()> {
132        assert_eq!(to_value(&i16::MAX)?, Value::from_i64(i64::from(i16::MAX)));
133        assert_eq!(to_value(&i16::MIN)?, Value::from_i64(i64::from(i16::MIN)));
134        Ok(())
135    }
136
137    #[test]
138    fn test_i32() -> anyhow::Result<()> {
139        assert_eq!(to_value(&i32::MAX)?, Value::from_i64(i64::from(i32::MAX)));
140        assert_eq!(to_value(&i32::MIN)?, Value::from_i64(i64::from(i32::MIN)));
141        Ok(())
142    }
143
144    #[test]
145    fn test_i64() -> anyhow::Result<()> {
146        assert_eq!(to_value(&i64::MAX)?, Value::from_i64(i64::MAX));
147        assert_eq!(to_value(&i64::MIN)?, Value::from_i64(i64::MIN));
148        Ok(())
149    }
150
151    #[test]
152    fn test_u8() -> anyhow::Result<()> {
153        assert_eq!(to_value(&u8::MAX)?, Value::from_i64(i64::from(u8::MAX)));
154        assert_eq!(to_value(&u8::MIN)?, Value::from_i64(i64::from(u8::MIN)));
155        Ok(())
156    }
157
158    #[test]
159    fn test_u16() -> anyhow::Result<()> {
160        assert_eq!(to_value(&u16::MAX)?, Value::from_i64(i64::from(u16::MAX)));
161        assert_eq!(to_value(&u16::MIN)?, Value::from_i64(i64::from(u16::MIN)));
162        Ok(())
163    }
164
165    #[test]
166    fn test_u32() -> anyhow::Result<()> {
167        assert_eq!(to_value(&u32::MAX)?, Value::from_i64(i64::from(u32::MAX)));
168        assert_eq!(to_value(&u32::MIN)?, Value::from_i64(i64::from(u32::MIN)));
169        Ok(())
170    }
171
172    #[test]
173    fn test_u64() -> anyhow::Result<()> {
174        assert_eq!(
175            to_value(&u64::MAX).unwrap_err().to_string(),
176            "u64 is not supported"
177        );
178        assert_eq!(
179            to_value(&u64::MIN).unwrap_err().to_string(),
180            "u64 is not supported"
181        );
182        Ok(())
183    }
184
185    #[test]
186    fn test_f32() -> anyhow::Result<()> {
187        assert_eq!(to_value(&f32::MAX)?, Value::from_f64(f64::from(f32::MAX)));
188        assert_eq!(to_value(&f32::MIN)?, Value::from_f64(f64::from(f32::MIN)));
189        Ok(())
190    }
191
192    #[test]
193    fn test_f64() -> anyhow::Result<()> {
194        assert_eq!(to_value(&f64::MAX)?, Value::from_f64(f64::MAX));
195        assert_eq!(to_value(&f64::MIN)?, Value::from_f64(f64::MIN));
196        Ok(())
197    }
198
199    #[test]
200    fn test_char() -> anyhow::Result<()> {
201        assert_eq!(to_value(&'a')?, Value::from_string("a".to_string()));
202        Ok(())
203    }
204
205    #[test]
206    fn test_str() -> anyhow::Result<()> {
207        assert_eq!(to_value(&"abc")?, Value::from_string("abc".to_string()));
208        assert_eq!(
209            to_value(&"a".repeat(MAX_BYTE_LEN))?,
210            Value::from_string("a".repeat(MAX_BYTE_LEN))
211        );
212        assert_eq!(
213            to_value(&"a".repeat(MAX_BYTE_LEN + 1))
214                .unwrap_err()
215                .to_string(),
216            "maximum byte length (1,048,487 bytes = 1MiB - 89 bytes) exceeded"
217        );
218
219        Ok(())
220    }
221
222    #[test]
223    fn test_bytes() -> anyhow::Result<()> {
224        assert_eq!(
225            to_value(&[0_u8, 1_u8])?,
226            // ArrayValue is used instead of BytesArray.
227            // See: <https://serde.rs/impl-serialize.html#other-special-cases>
228            Value::from_values(vec![Value::from_i64(0_i64), Value::from_i64(1_i64)])
229        );
230        // ArrayValue length is not checked.
231        // assert!(to_value(&vec![0_u8; MAX_BYTE_LEN]).is_ok());
232        // assert_eq!(
233        //     to_value(&vec![0_u8; MAX_BYTE_LEN + 1])
234        //         .unwrap_err()
235        //         .to_string(),
236        //     "maximum byte length (1,048,487 bytes = 1MiB - 89 bytes) exceeded"
237        // );
238        Ok(())
239    }
240
241    #[test]
242    fn test_none() -> anyhow::Result<()> {
243        assert_eq!(to_value(&None::<Option<i64>>)?, Value::null());
244        Ok(())
245    }
246
247    #[test]
248    fn test_some() -> anyhow::Result<()> {
249        assert_eq!(to_value(&Some(true))?, Value::from_bool(true));
250        assert_eq!(to_value(&Some(1_i64))?, Value::from_i64(1_i64));
251        Ok(())
252    }
253
254    #[test]
255    fn test_unit() -> anyhow::Result<()> {
256        assert_eq!(to_value(&())?, Value::null());
257        Ok(())
258    }
259
260    #[test]
261    fn test_unit_struct() -> anyhow::Result<()> {
262        #[derive(serde::Serialize)]
263        struct Unit;
264        assert_eq!(to_value(&Unit)?, Value::null());
265        Ok(())
266    }
267
268    #[test]
269    fn test_unit_variant() -> anyhow::Result<()> {
270        #[derive(serde::Serialize)]
271        enum E {
272            A,
273            B,
274        }
275        assert_eq!(to_value(&E::A)?, Value::from_string("A".to_string()));
276        assert_eq!(to_value(&E::B)?, Value::from_string("B".to_string()));
277        Ok(())
278    }
279
280    #[test]
281    fn test_newtype_struct() -> anyhow::Result<()> {
282        #[derive(serde::Serialize)]
283        struct Millimeters(u8);
284        assert_eq!(
285            to_value(&Millimeters(u8::MAX))?,
286            Value::from_i64(i64::from(u8::MAX))
287        );
288        Ok(())
289    }
290
291    #[test]
292    fn test_newtype_variant() -> anyhow::Result<()> {
293        #[derive(serde::Serialize)]
294        enum E {
295            N(u8),
296        }
297        assert_eq!(
298            to_value(&E::N(u8::MAX))?,
299            Value::from_fields([("N", Value::from_i64(i64::from(u8::MAX)))])
300        );
301        Ok(())
302    }
303
304    #[test]
305    fn test_seq() -> anyhow::Result<()> {
306        assert_eq!(
307            to_value(&vec![1_i64, 2_i64, 3_i64])?,
308            Value::from_values(vec![
309                Value::from_i64(1_i64),
310                Value::from_i64(2_i64),
311                Value::from_i64(3_i64)
312            ])
313        );
314        assert_eq!(
315            to_value(&vec![vec![1_i64]])?,
316            Value::from_values(vec![Value::from_values(vec![Value::from_i64(1_i64)])])
317        );
318        Ok(())
319    }
320
321    #[test]
322    fn test_tuple() -> anyhow::Result<()> {
323        assert_eq!(
324            to_value(&(true, 1, "abc"))?,
325            Value::from_values(vec![
326                Value::from_bool(true),
327                Value::from_i64(1),
328                Value::from_string("abc".to_string())
329            ])
330        );
331        Ok(())
332    }
333
334    #[test]
335    fn test_tuple_struct() -> anyhow::Result<()> {
336        #[derive(serde::Serialize)]
337        struct Rgb(u8, u8, u8);
338        assert_eq!(
339            to_value(&Rgb(1, 2, 3))?,
340            Value::from_values(vec![
341                Value::from_i64(1),
342                Value::from_i64(2),
343                Value::from_i64(3)
344            ])
345        );
346        Ok(())
347    }
348
349    #[test]
350    fn test_tuple_variant() -> anyhow::Result<()> {
351        #[derive(serde::Serialize)]
352        enum E {
353            T(u8, u8),
354        }
355        assert_eq!(
356            to_value(&E::T(1, 2))?,
357            Value::from_fields([(
358                "T",
359                Value::from_values(vec![Value::from_i64(1), Value::from_i64(2)]),
360            )])
361        );
362        Ok(())
363    }
364
365    #[test]
366    fn test_map() -> anyhow::Result<()> {
367        assert_eq!(
368            to_value(&{
369                let mut map = std::collections::HashMap::new();
370                map.insert("k1", 1_i64);
371                map.insert("k2", 2_i64);
372                map
373            })?,
374            Value::from_fields([("k1", Value::from_i64(1)), ("k2", Value::from_i64(2))])
375        );
376        Ok(())
377    }
378
379    #[test]
380    fn test_struct() -> anyhow::Result<()> {
381        #[derive(serde::Serialize)]
382        struct S {
383            r: u8,
384            g: u8,
385            b: u8,
386        }
387        assert_eq!(
388            to_value(&S { r: 1, g: 2, b: 3 })?,
389            Value::from_fields([
390                ("r", Value::from_i64(1)),
391                ("g", Value::from_i64(2)),
392                ("b", Value::from_i64(3)),
393            ])
394        );
395        Ok(())
396    }
397
398    #[test]
399    fn test_struct_variant() -> anyhow::Result<()> {
400        #[derive(serde::Serialize)]
401        enum E {
402            S { r: u8, g: u8, b: u8 },
403        }
404        assert_eq!(
405            to_value(&E::S { r: 1, g: 2, b: 3 })?,
406            Value::from_fields([(
407                "S",
408                Value::from_fields([
409                    ("r", Value::from_i64(1)),
410                    ("g", Value::from_i64(2)),
411                    ("b", Value::from_i64(3)),
412                ]),
413            )])
414        );
415        Ok(())
416    }
417
418    #[test]
419    fn test_error_key_must_be_a_string() -> anyhow::Result<()> {
420        assert_eq!(
421            to_value(&{
422                let mut map = HashMap::new();
423                map.insert((), 1_u8);
424                map
425            })
426            .unwrap_err()
427            .to_string(),
428            "key must be a string"
429        );
430        assert_eq!(
431            to_value(&{
432                let mut map = BTreeMap::new();
433                map.insert('a', 1_u8);
434                map
435            })?,
436            Value::from_fields([("a", Value::from_i64(1))])
437        );
438        Ok(())
439    }
440
441    #[test]
442    fn test_impl_serde_ser_error() {
443        fn assert_impl<T: serde::ser::Error>() {}
444        assert_impl::<Error>();
445        assert_eq!(
446            <Error as serde::ser::Error>::custom("custom error").to_string(),
447            "custom error"
448        );
449    }
450}