cbor_data/value/
mod.rs

1use self::{CborValue::*, Number::*};
2use crate::{constants::*, Cbor, CborOwned, ItemKind, TaggedItem};
3use std::{
4    borrow::Cow,
5    collections::{btree_map::Entry, BTreeMap},
6    fmt::Debug,
7};
8
9mod number;
10mod timestamp;
11
12pub use number::{Exponential, Number};
13pub use timestamp::{Precision, Timestamp};
14
15/// Lifted navigation structure for a CborValue.
16///
17/// You can obtain this using [`Cbor::decode()`](../struct.Cbor.html#method.decode).
18/// This will reference existing bytes as much as possible, but in some cases it
19/// has to allocate, e.g. when some needed slices are not contiguous in the underlying
20/// `Cbor`.
21#[derive(Debug, Clone, PartialEq)]
22pub enum CborValue<'a> {
23    Array(Vec<Cow<'a, Cbor>>),
24    Dict(BTreeMap<Cow<'a, Cbor>, Cow<'a, Cbor>>),
25    Undefined,
26    Null,
27    Bool(bool),
28    Number(Number<'a>),
29    Timestamp(Timestamp),
30    Str(Cow<'a, str>),
31    Bytes(Cow<'a, [u8]>),
32    /// Structural constraints for a tag are violated (like tag 0 on a number)
33    Invalid,
34    /// Unknown tags are present, you may want to manually interpret the TaggedItem
35    Unknown,
36}
37
38impl<'a> CborValue<'a> {
39    pub fn new(item: TaggedItem<'a>) -> Self {
40        Self::from_item(item).unwrap_or(Invalid)
41    }
42
43    fn from_item(item: TaggedItem<'a>) -> Option<Self> {
44        match item.tags().single() {
45            #[cfg(feature = "rfc3339")]
46            Some(TAG_ISO8601) => Timestamp::from_string(item).map(Timestamp),
47            Some(TAG_EPOCH) => Timestamp::from_epoch(item).map(Timestamp),
48            Some(TAG_BIGNUM_POS | TAG_BIGNUM_NEG) => {
49                Some(Number(Decimal(Exponential::from_bytes(item)?)))
50            }
51            Some(TAG_BIGDECIMAL | TAG_BIGFLOAT) => Number::from_bignum(item).map(CborValue::Number),
52            Some(TAG_CBOR_ITEM) => {
53                if let ItemKind::Bytes(b) = item.kind() {
54                    if let Some(b) = b.as_slice() {
55                        Some(Cbor::unchecked(b).decode())
56                    } else {
57                        Some(CborOwned::unchecked(b.to_vec()).decode().make_static())
58                    }
59                } else {
60                    None
61                }
62            }
63            Some(t @ (TAG_BASE64 | TAG_BASE64URL)) => {
64                if let ItemKind::Str(s) = item.kind() {
65                    let s = s.as_cow();
66                    let b = if t == TAG_BASE64 {
67                        base64::decode(s.as_bytes())
68                    } else {
69                        base64::decode_config(s.as_bytes(), base64::URL_SAFE_NO_PAD)
70                    };
71                    b.map(|bytes| Bytes(Cow::Owned(bytes))).ok()
72                } else {
73                    None
74                }
75            }
76            None => Some(match item.kind() {
77                ItemKind::Pos(x) => Number(Int(x.into())),
78                ItemKind::Neg(x) => Number(Int(-1_i128 - i128::from(x))),
79                ItemKind::Float(f) => Number(IEEE754(f)),
80                ItemKind::Str(s) => Str(s.as_cow()),
81                ItemKind::Bytes(b) => Bytes(b.as_cow()),
82                ItemKind::Bool(b) => Bool(b),
83                ItemKind::Null => Null,
84                ItemKind::Undefined => Undefined,
85                ItemKind::Simple(_) => Unknown,
86                ItemKind::Array(a) => Array(a.map(Cow::Borrowed).collect()),
87                ItemKind::Dict(d) => Dict(d.fold(BTreeMap::new(), |mut acc, (k, v)| {
88                    if let Entry::Vacant(e) = acc.entry(Cow::Borrowed(k)) {
89                        e.insert(Cow::Borrowed(v));
90                    }
91                    acc
92                })),
93            }),
94            _ => Some(Unknown),
95        }
96    }
97
98    pub fn is_undefined(&self) -> bool {
99        matches!(self, Undefined)
100    }
101
102    pub fn is_null(&self) -> bool {
103        matches!(self, Null)
104    }
105
106    pub fn is_unknown(&self) -> bool {
107        matches!(self, Unknown)
108    }
109
110    pub fn is_invalid(&self) -> bool {
111        matches!(self, Invalid)
112    }
113
114    pub fn as_bool(&self) -> Option<bool> {
115        if let Bool(b) = self {
116            Some(*b)
117        } else {
118            None
119        }
120    }
121
122    pub fn as_number(&self) -> Option<&Number> {
123        if let Number(n) = self {
124            Some(n)
125        } else {
126            None
127        }
128    }
129
130    pub fn to_number(self) -> Option<Number<'a>> {
131        if let Number(n) = self {
132            Some(n)
133        } else {
134            None
135        }
136    }
137
138    pub fn as_timestamp(&self) -> Option<Timestamp> {
139        if let Timestamp(t) = self {
140            Some(*t)
141        } else {
142            None
143        }
144    }
145
146    pub fn as_str(&self) -> Option<&Cow<'a, str>> {
147        if let Str(s) = self {
148            Some(s)
149        } else {
150            None
151        }
152    }
153
154    pub fn to_str(self) -> Option<Cow<'a, str>> {
155        if let Str(s) = self {
156            Some(s)
157        } else {
158            None
159        }
160    }
161
162    pub fn as_bytes(&self) -> Option<&Cow<'a, [u8]>> {
163        if let Bytes(b) = self {
164            Some(b)
165        } else {
166            None
167        }
168    }
169
170    pub fn to_bytes(self) -> Option<Cow<'a, [u8]>> {
171        if let Bytes(b) = self {
172            Some(b)
173        } else {
174            None
175        }
176    }
177
178    pub fn as_array(&self) -> Option<&[Cow<'a, Cbor>]> {
179        if let Array(a) = self {
180            Some(a.as_slice())
181        } else {
182            None
183        }
184    }
185
186    pub fn to_array(self) -> Option<Vec<Cow<'a, Cbor>>> {
187        if let Array(a) = self {
188            Some(a)
189        } else {
190            None
191        }
192    }
193
194    pub fn as_dict(&self) -> Option<&BTreeMap<Cow<'a, Cbor>, Cow<'a, Cbor>>> {
195        if let Dict(a) = self {
196            Some(a)
197        } else {
198            None
199        }
200    }
201
202    pub fn to_dict(self) -> Option<BTreeMap<Cow<'a, Cbor>, Cow<'a, Cbor>>> {
203        if let Dict(a) = self {
204            Some(a)
205        } else {
206            None
207        }
208    }
209
210    /// Cut all ties to the underlying byte slice, which often implies allocations
211    pub fn make_static(self) -> CborValue<'static> {
212        match self {
213            Array(a) => Array(a.into_iter().map(ms).collect()),
214            Dict(d) => Dict(d.into_iter().map(|(k, v)| (ms(k), ms(v))).collect()),
215            Undefined => Undefined,
216            Null => Null,
217            Bool(b) => Bool(b),
218            Number(n) => Number(n.make_static()),
219            Timestamp(t) => Timestamp(t),
220            Str(s) => Str(ms(s)),
221            Bytes(b) => Bytes(ms(b)),
222            Invalid => Invalid,
223            Unknown => Unknown,
224        }
225    }
226}
227
228fn ms<'a, T: ToOwned + ?Sized + 'a>(c: Cow<'a, T>) -> Cow<'static, T> {
229    match c {
230        Cow::Borrowed(b) => Cow::Owned(b.to_owned()),
231        Cow::Owned(o) => Cow::Owned(o),
232    }
233}
234
235#[cfg(test)]
236mod tests {
237    use crate::{
238        constants::*,
239        value::{number::Exponential, Number, Timestamp},
240        CborBuilder, CborOwned, CborValue, Encoder, Literal, Writer,
241    };
242
243    #[test]
244    fn test_str_lifetime() {
245        fn _check_compile<'a, 'err: 'a>(value: &'a CborValue<'err>) -> &'err str {
246            match value.as_str().unwrap() {
247                std::borrow::Cow::Borrowed(b) => *b,
248                std::borrow::Cow::Owned(_) => todo!(),
249            }
250        }
251    }
252
253    #[test]
254    fn test_bytes_lifetime() {
255        fn _check_compile<'a, 'err: 'a>(value: &'a CborValue<'err>) -> &'err [u8] {
256            match value.as_bytes().unwrap() {
257                std::borrow::Cow::Borrowed(b) => *b,
258                std::borrow::Cow::Owned(_) => todo!(),
259            }
260        }
261    }
262
263    #[test]
264    fn display() {
265        fn to_cbor_str(f: f64) -> String {
266            format!("{}", CborBuilder::new().encode_f64(f))
267        }
268        assert_eq!(to_cbor_str(1.0), "1.0");
269        assert_eq!(to_cbor_str(-1.1), "-1.1");
270        assert_eq!(to_cbor_str(0.0), "0.0");
271        assert_eq!(to_cbor_str(-0.0), "-0.0");
272    }
273
274    #[test]
275    fn base64string() {
276        fn to_cbor(s: &str, tag: u64) -> CborOwned {
277            let mut v = vec![0xd8u8, tag as u8, 0x60 | (s.len() as u8)];
278            v.extend_from_slice(s.as_bytes());
279            CborOwned::unchecked(v)
280        }
281        fn b(bytes: &CborOwned) -> Vec<u8> {
282            if let CborValue::Bytes(bytes) = bytes.decode() {
283                bytes.into_owned()
284            } else {
285                panic!("no bytes: {}", bytes)
286            }
287        }
288
289        let bytes = to_cbor("a346_-0=", TAG_BASE64URL);
290        assert_eq!(b(&bytes), vec![107, 126, 58, 255, 237]);
291
292        let bytes = to_cbor("a346_-0", TAG_BASE64URL);
293        assert_eq!(b(&bytes), vec![107, 126, 58, 255, 237]);
294
295        let bytes = to_cbor("a346/+0=", TAG_BASE64);
296        assert_eq!(b(&bytes), vec![107, 126, 58, 255, 237]);
297
298        let bytes = to_cbor("a346/+0", TAG_BASE64);
299        assert_eq!(b(&bytes), vec![107, 126, 58, 255, 237]);
300    }
301
302    #[test]
303    fn tags() {
304        let cbor = CborBuilder::new().write_null([1, 2, 3]);
305        assert_eq!(cbor.tags().last(), Some(3));
306        assert_eq!(cbor.tags().first(), Some(1));
307        assert_eq!(cbor.tags().single(), None);
308
309        let cbor = CborBuilder::new().write_null([4]);
310        assert_eq!(cbor.tags().last(), Some(4));
311        assert_eq!(cbor.tags().first(), Some(4));
312        assert_eq!(cbor.tags().single(), Some(4));
313    }
314
315    #[test]
316    #[cfg(feature = "rfc3339")]
317    fn rfc3339() {
318        let cbor = CborBuilder::new().write_str("1983-03-22T12:17:05.345+02:00", [TAG_ISO8601]);
319        assert_eq!(
320            cbor.decode(),
321            CborValue::Timestamp(Timestamp::new(417176225, 345_000_000, 7200))
322        );
323
324        let cbor = CborBuilder::new().write_str("2183-03-22T12:17:05.345-03:00", [TAG_ISO8601]);
325        assert_eq!(
326            cbor.decode(),
327            CborValue::Timestamp(Timestamp::new(6728627825, 345_000_000, -10800))
328        );
329
330        let cbor = CborBuilder::new().write_str("1833-03-22T02:17:05.345-13:00", [TAG_ISO8601]);
331        assert_eq!(
332            cbor.decode(),
333            CborValue::Timestamp(Timestamp::new(-4316316175, 345_000_000, -46800))
334        );
335    }
336
337    #[test]
338    fn epoch() {
339        let cbor = CborBuilder::new().write_pos(1234567, [TAG_EPOCH]);
340        assert_eq!(
341            cbor.decode(),
342            CborValue::Timestamp(Timestamp::new(1234567, 0, 0))
343        );
344
345        let cbor = CborBuilder::new().write_neg(1234566, [TAG_EPOCH]);
346        assert_eq!(
347            cbor.decode(),
348            CborValue::Timestamp(Timestamp::new(-1234567, 0, 0))
349        );
350
351        let cbor = CborBuilder::new()
352            .write_lit(Literal::L8(2_345.900_000_014_5_f64.to_bits()), [TAG_EPOCH]);
353        assert_eq!(
354            cbor.decode(),
355            CborValue::Timestamp(Timestamp::new(2345, 900_000_015, 0))
356        );
357
358        let cbor = CborBuilder::new()
359            .write_lit(Literal::L8(2_345.900_000_015_5_f64.to_bits()), [TAG_EPOCH]);
360        assert_eq!(
361            cbor.decode(),
362            CborValue::Timestamp(Timestamp::new(2345, 900_000_016, 0))
363        );
364    }
365
366    #[test]
367    fn bignum() {
368        let cbor = CborBuilder::new().write_array([TAG_BIGFLOAT], |b| {
369            b.write_neg(2, []);
370            b.write_pos(13, []);
371        });
372        assert_eq!(
373            cbor.decode(),
374            CborValue::Number(Number::Float(Exponential::new(
375                -3,
376                [13_u8][..].into(),
377                false,
378            )))
379        );
380
381        let cbor = CborBuilder::new().write_array([TAG_BIGFLOAT], |b| {
382            b.write_neg(2, []);
383            b.write_neg(12, []);
384        });
385        assert_eq!(
386            cbor.decode(),
387            CborValue::Number(Number::Float(Exponential::new(
388                -3,
389                [12_u8][..].into(),
390                true,
391            )))
392        );
393
394        let cbor = CborBuilder::new().write_array([TAG_BIGFLOAT], |b| {
395            b.write_neg(2, []);
396            b.write_pos(0x010203, []);
397        });
398        assert_eq!(
399            cbor.decode(),
400            CborValue::Number(Number::Float(Exponential::new(
401                -3,
402                [1, 2, 3][..].into(),
403                false,
404            )))
405        );
406
407        let cbor = CborBuilder::new().write_array([TAG_BIGFLOAT], |b| {
408            b.write_neg(2, []);
409            b.write_bytes([1, 2, 3].as_ref(), [TAG_BIGNUM_POS]);
410        });
411        assert_eq!(
412            cbor.decode(),
413            CborValue::Number(Number::Float(Exponential::new(
414                -3,
415                [1, 2, 3][..].into(),
416                false,
417            )))
418        );
419
420        let cbor = CborBuilder::new().write_array([TAG_BIGFLOAT], |b| {
421            b.write_neg(2, []);
422            b.write_neg(0x010203, []);
423        });
424        assert_eq!(
425            cbor.decode(),
426            CborValue::Number(Number::Float(Exponential::new(
427                -3,
428                [1, 2, 3][..].into(),
429                true,
430            )))
431        );
432
433        let cbor = CborBuilder::new().write_array([TAG_BIGFLOAT], |b| {
434            b.write_neg(2, []);
435            b.write_bytes([1, 2, 3].as_ref(), [TAG_BIGNUM_NEG]);
436        });
437        assert_eq!(
438            cbor.decode(),
439            CborValue::Number(Number::Float(Exponential::new(
440                -3,
441                [1, 2, 3][..].into(),
442                true,
443            )))
444        );
445
446        let cbor = CborBuilder::new().write_array([TAG_BIGDECIMAL], |b| {
447            b.write_pos(2, []);
448            b.write_pos(0xff01020304, []);
449        });
450        assert_eq!(
451            cbor.decode(),
452            CborValue::Number(Number::Decimal(Exponential::new(
453                2,
454                [255, 1, 2, 3, 4][..].into(),
455                false,
456            )))
457        );
458    }
459}