cbor/
value.rs

1// This Source Code Form is subject to the terms of
2// the Mozilla Public License, v. 2.0. If a copy of
3// the MPL was not distributed with this file, You
4// can obtain one at http://mozilla.org/MPL/2.0/.
5
6//! This module defines the generic `Value` AST as well as
7//! several  other types to represent CBOR values.
8//! A `Cursor` can be used to deconstruct and traverse
9//! a `Value`.
10
11use std::collections::{BTreeMap, LinkedList};
12use std::i64;
13use types::Tag;
14
15/// The generic CBOR representation.
16#[derive(Clone, Debug, PartialEq, PartialOrd)]
17pub enum Value {
18    Array(Vec<Value>),
19    Bool(bool),
20    Break,
21    Bytes(Bytes),
22    F32(f32),
23    F64(f64),
24    I8(i8),
25    I16(i16),
26    I32(i32),
27    I64(i64),
28    Int(Int),
29    Map(BTreeMap<Key, Value>),
30    Null,
31    Simple(Simple),
32    Tagged(Tag, Box<Value>),
33    Text(Text),
34    U8(u8),
35    U16(u16),
36    U32(u32),
37    U64(u64),
38    Undefined
39}
40
41/// Type to represent all possible CBOR integer values.
42///
43/// Since the encoding of negative integers (major type 1) follows
44/// unsigned integers (major type 0), mapping negative integers
45/// to `i8`, `i16`, `i32` or `i64` can result in integer overflows.
46/// If all possible values should be handled, this type can be used.
47#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
48pub enum Int {
49    Neg(u64),
50    Pos(u64)
51}
52
53impl Int {
54    pub fn from_u64(n: u64) -> Int {
55        Int::Pos(n)
56    }
57
58    pub fn from_i64(n: i64) -> Int {
59        if n < 0 {
60            Int::Neg(i64::abs(n) as u64 - 1)
61        } else {
62            Int::Pos(n as u64)
63        }
64    }
65
66    /// Map this value to an `i64`. If the value does not
67    /// fit within `[i64::MIN, i64::MAX]`, `None` is returned instead.
68    pub fn i64(&self) -> Option<i64> {
69        match *self {
70            Int::Neg(n) if n <= i64::MAX as u64 => Some(-1 - n as i64),
71            Int::Pos(n) if n <= i64::MAX as u64 => Some(n as i64),
72            _ => None
73        }
74    }
75
76    /// Map this value to a `u64`. If the value is negative,
77    /// `None` is returned instead.
78    pub fn u64(&self) -> Option<u64> {
79        match *self {
80            Int::Pos(n) => Some(n),
81            _           => None
82        }
83    }
84}
85
86/// A unification of plain and indefinitly sized strings.
87#[derive(Clone, PartialEq, PartialOrd, Eq, Ord, Debug, Hash)]
88pub enum Text {
89    Text(String),
90    Chunks(LinkedList<String>)
91}
92
93/// A unification of plain an indefinitly sized byte strings.
94#[derive(Clone, PartialEq, PartialOrd, Eq, Ord, Debug, Hash)]
95pub enum Bytes {
96    Bytes(Vec<u8>),
97    Chunks(LinkedList<Vec<u8>>)
98}
99
100/// Most simple types (e.g. `bool` are covered elsewhere) but this
101/// value captures those value ranges of CBOR type `Simple` (major 7)
102/// which are either not assigned or reserved.
103#[derive(Clone, Copy, PartialEq, PartialOrd, Eq, Ord, Debug, Hash)]
104pub enum Simple {
105    Unassigned(u8),
106    Reserved(u8)
107}
108
109/// CBOR allows heterogenous keys in objects. This enum unifies
110/// all currently allowed key types.
111#[derive(Clone, Debug, PartialEq, PartialOrd, Eq, Ord, Hash)]
112pub enum Key {
113    Bool(bool),
114    Bytes(Bytes),
115    Int(Int),
116    Text(Text)
117}
118
119impl Key {
120    pub fn u64(n: u64) -> Key {
121        Key::Int(Int::from_u64(n))
122    }
123
124    pub fn i64(n: i64) -> Key {
125        Key::Int(Int::from_i64(n))
126    }
127}
128
129/// A `Cursor` allows conventient navigation in a `Value` AST.
130/// `Value`s can be converted to native Rust types if possible and
131/// collections can be traversed using `at` or `get`.
132pub struct Cursor<'r> {
133    value: Option<&'r Value>
134}
135
136impl<'r> Cursor<'r> {
137    pub fn new(v: &'r Value) -> Cursor<'r> {
138        Cursor { value: Some(v) }
139    }
140
141    fn of(v: Option<&'r Value>) -> Cursor<'r> {
142        Cursor { value: v }
143    }
144
145    pub fn at(&self, i: usize) -> Cursor<'r> {
146        match self.value {
147            Some(&Value::Array(ref a)) => Cursor::of(a.get(i)),
148            _                          => Cursor::of(None)
149        }
150    }
151
152    pub fn get(&self, k: Key) -> Cursor<'r> {
153        match self.value {
154            Some(&Value::Map(ref m)) => Cursor::of(m.get(&k)),
155            _                        => Cursor::of(None)
156        }
157    }
158
159    pub fn field(&self, s: &str) -> Cursor<'r> {
160        self.get(Key::Text(Text::Text(String::from(s))))
161    }
162
163    pub fn value(&self) -> Option<&Value> {
164        self.value
165    }
166
167    pub fn opt(&self) -> Option<Cursor<'r>> {
168        match self.value {
169            Some(&Value::Null) => None,
170            Some(ref v)        => Some(Cursor::new(v)),
171            _                  => None
172        }
173    }
174
175    pub fn maybe(&self) -> Option<Cursor<'r>> {
176        match self.value {
177            Some(&Value::Undefined) => None,
178            Some(ref v)             => Some(Cursor::new(v)),
179            _                       => None
180        }
181    }
182
183    pub fn bool(&self) -> Option<bool> {
184        match self.value {
185            Some(&Value::Bool(x)) => Some(x),
186            _                     => None
187        }
188    }
189
190    pub fn bytes(&self) -> Option<&Bytes> {
191        match self.value {
192            Some(&Value::Bytes(ref x)) => Some(x),
193            _                          => None
194        }
195    }
196
197    pub fn bytes_plain(&self) -> Option<&Vec<u8>> {
198        match self.value {
199            Some(&Value::Bytes(Bytes::Bytes(ref x))) => Some(x),
200            _                                        => None
201        }
202    }
203
204    pub fn bytes_chunked(&self) -> Option<&LinkedList<Vec<u8>>> {
205        match self.value {
206            Some(&Value::Bytes(Bytes::Chunks(ref x))) => Some(x),
207            _                                         => None
208        }
209    }
210
211    pub fn text(&self) -> Option<&Text> {
212        match self.value {
213            Some(&Value::Text(ref x)) => Some(x),
214            _                         => None
215        }
216    }
217
218    pub fn text_plain(&self) -> Option<&String> {
219        match self.value {
220            Some(&Value::Text(Text::Text(ref x))) => Some(x),
221            _                                     => None
222        }
223    }
224
225    pub fn text_chunked(&self) -> Option<&LinkedList<String>> {
226        match self.value {
227            Some(&Value::Text(Text::Chunks(ref x))) => Some(x),
228            _                                       => None
229        }
230    }
231
232    pub fn float32(&self) -> Option<f32> {
233        match self.value {
234            Some(&Value::F32(x)) => Some(x),
235            _                    => None
236        }
237    }
238
239    pub fn float64(&self) -> Option<f64> {
240        match self.value {
241            Some(&Value::F64(x)) => Some(x),
242            _                    => None
243        }
244    }
245
246    pub fn u8(&self) -> Option<u8> {
247        match self.value {
248            Some(&Value::U8(x)) => Some(x),
249            _                   => None
250        }
251    }
252
253    pub fn u16(&self) -> Option<u16> {
254        match self.value {
255            Some(&Value::U16(x)) => Some(x),
256            _                    => None
257        }
258    }
259
260    pub fn u32(&self) -> Option<u32> {
261        match self.value {
262            Some(&Value::U32(x)) => Some(x),
263            _                    => None
264        }
265    }
266
267    pub fn u64(&self) -> Option<u64> {
268        match self.value {
269            Some(&Value::U64(x)) => Some(x),
270            _                    => None
271        }
272    }
273
274    pub fn i8(&self) -> Option<i8> {
275        match self.value {
276            Some(&Value::I8(x)) => Some(x),
277            _                   => None
278        }
279    }
280
281    pub fn i16(&self) -> Option<i16> {
282        match self.value {
283            Some(&Value::I16(x)) => Some(x),
284            _                    => None
285        }
286    }
287
288    pub fn i32(&self) -> Option<i32> {
289        match self.value {
290            Some(&Value::I32(x)) => Some(x),
291            _                    => None
292        }
293    }
294
295    pub fn i64(&self) -> Option<i64> {
296        match self.value {
297            Some(&Value::I64(x)) => Some(x),
298            _                    => None
299        }
300    }
301}
302
303/// Inspect the given `Value` which must be a `Value::Tagged` and
304/// ensure that the `Tag` and type of value match according to
305/// RFC 7049 section 2.4
306pub fn check(value: &Value) -> bool {
307    fn fun(t: Tag, b: &Value) -> bool {
308        match (t, b) {
309            (Tag::DateTime, &Value::Text(_))        => true,
310            (Tag::Timestamp, &Value::U8(_))         => true,
311            (Tag::Timestamp, &Value::U16(_))        => true,
312            (Tag::Timestamp, &Value::U32(_))        => true,
313            (Tag::Timestamp, &Value::U64(_))        => true,
314            (Tag::Timestamp, &Value::I8(_))         => true,
315            (Tag::Timestamp, &Value::I16(_))        => true,
316            (Tag::Timestamp, &Value::I32(_))        => true,
317            (Tag::Timestamp, &Value::I64(_))        => true,
318            (Tag::Timestamp, &Value::F32(_))        => true,
319            (Tag::Timestamp, &Value::F64(_))        => true,
320            (Tag::Bignum, &Value::Bytes(_))         => true,
321            (Tag::NegativeBignum, &Value::Bytes(_)) => true,
322            (Tag::ToBase64, _)                      => true,
323            (Tag::ToBase64Url, _)                   => true,
324            (Tag::ToBase16, _)                      => true,
325            (Tag::Cbor, &Value::Bytes(_))           => true,
326            (Tag::Uri, &Value::Text(_))             => true,
327            (Tag::Base64, &Value::Text(_))          => true,
328            (Tag::Base64Url, &Value::Text(_))       => true,
329            (Tag::Regex, &Value::Text(_))           => true,
330            (Tag::Mime, &Value::Text(_))            => true,
331            (Tag::CborSelf, _)                      => true,
332            (Tag::Decimal, &Value::Array(ref a))
333            | (Tag::Bigfloat, &Value::Array(ref a)) => {
334                if a.len() != 2 {
335                    return false
336                }
337                let is_integral = |v: &Value| {
338                    match *v {
339                        Value::U8(_) | Value::U16(_) | Value::U32(_) | Value::U64(_) => true,
340                        Value::I8(_) | Value::I16(_) | Value::I32(_) | Value::I64(_) => true,
341                        _                                                            => false
342                    }
343                };
344                let is_bignum = |v: &Value| {
345                    fun(Tag::Bignum, v) || fun(Tag::NegativeBignum, v)
346                };
347                let ref e = a[0];
348                let ref m = a[1];
349                is_integral(e) && (is_integral(m) || is_bignum(m))
350            }
351            (Tag::Unassigned(_), _) => true,
352            _                       => false
353        }
354    }
355
356    match *value {
357        Value::Tagged(t, ref b) => fun(t, &*b),
358        _                       => false
359    }
360}