ciborium/value/
mod.rs

1// SPDX-License-Identifier: Apache-2.0
2
3//! A dynamic CBOR value
4
5mod integer;
6
7mod de;
8mod error;
9mod ser;
10
11pub use error::Error;
12pub use integer::Integer;
13
14use alloc::{boxed::Box, string::String, vec::Vec};
15
16/// A representation of a dynamic CBOR value that can handled dynamically
17#[non_exhaustive]
18#[derive(Clone, Debug, PartialEq, PartialOrd)]
19pub enum Value {
20    /// An integer
21    Integer(Integer),
22
23    /// Bytes
24    Bytes(Vec<u8>),
25
26    /// A float
27    Float(f64),
28
29    /// A string
30    Text(String),
31
32    /// A boolean
33    Bool(bool),
34
35    /// Null
36    Null,
37
38    /// Tag
39    Tag(u64, Box<Value>),
40
41    /// An array
42    Array(Vec<Value>),
43
44    /// A map
45    Map(Vec<(Value, Value)>),
46}
47
48impl Value {
49    /// Returns true if the `Value` is an `Integer`. Returns false otherwise.
50    ///
51    /// ```
52    /// # use ciborium::value::Value;
53    /// #
54    /// let value = Value::Integer(17.into());
55    ///
56    /// assert!(value.is_integer());
57    /// ```
58    pub fn is_integer(&self) -> bool {
59        self.as_integer().is_some()
60    }
61
62    /// If the `Value` is a `Integer`, returns a reference to the associated `Integer` data.
63    /// Returns None otherwise.
64    ///
65    /// ```
66    /// # use ciborium::value::Value;
67    /// #
68    /// let value = Value::Integer(17.into());
69    ///
70    /// // We can read the number
71    /// assert_eq!(17, value.as_integer().unwrap().try_into().unwrap());
72    /// ```
73    pub fn as_integer(&self) -> Option<Integer> {
74        match self {
75            Value::Integer(int) => Some(*int),
76            _ => None,
77        }
78    }
79
80    /// Returns true if the `Value` is a `Bytes`. Returns false otherwise.
81    ///
82    /// ```
83    /// # use ciborium::value::Value;
84    /// #
85    /// let value = Value::Bytes(vec![104, 101, 108, 108, 111]);
86    ///
87    /// assert!(value.is_bytes());
88    /// ```
89    pub fn is_bytes(&self) -> bool {
90        self.as_bytes().is_some()
91    }
92
93    /// If the `Value` is a `Bytes`, returns a reference to the associated bytes vector.
94    /// Returns None otherwise.
95    ///
96    /// ```
97    /// # use ciborium::value::Value;
98    /// #
99    /// let value = Value::Bytes(vec![104, 101, 108, 108, 111]);
100    ///
101    /// assert_eq!(std::str::from_utf8(value.as_bytes().unwrap()).unwrap(), "hello");
102    /// ```
103    pub fn as_bytes(&self) -> Option<&Vec<u8>> {
104        match *self {
105            Value::Bytes(ref bytes) => Some(bytes),
106            _ => None,
107        }
108    }
109
110    /// If the `Value` is a `Bytes`, returns a mutable reference to the associated bytes vector.
111    /// Returns None otherwise.
112    ///
113    /// ```
114    /// # use ciborium::value::Value;
115    /// #
116    /// let mut value = Value::Bytes(vec![104, 101, 108, 108, 111]);
117    /// value.as_bytes_mut().unwrap().clear();
118    ///
119    /// assert_eq!(value, Value::Bytes(vec![]));
120    /// ```
121    pub fn as_bytes_mut(&mut self) -> Option<&mut Vec<u8>> {
122        match *self {
123            Value::Bytes(ref mut bytes) => Some(bytes),
124            _ => None,
125        }
126    }
127
128    /// Returns true if the `Value` is a `Float`. Returns false otherwise.
129    ///
130    /// ```
131    /// # use ciborium::value::Value;
132    /// #
133    /// let value = Value::Float(17.0.into());
134    ///
135    /// assert!(value.is_float());
136    /// ```
137    pub fn is_float(&self) -> bool {
138        self.as_float().is_some()
139    }
140
141    /// If the `Value` is a `Float`, returns a reference to the associated float data.
142    /// Returns None otherwise.
143    ///
144    /// ```
145    /// # use ciborium::value::Value;
146    /// #
147    /// let value = Value::Float(17.0.into());
148    ///
149    /// // We can read the float number
150    /// assert_eq!(value.as_float().unwrap(), 17.0_f64);
151    /// ```
152    pub fn as_float(&self) -> Option<f64> {
153        match *self {
154            Value::Float(f) => Some(f),
155            _ => None,
156        }
157    }
158
159    /// Returns true if the `Value` is a `Text`. Returns false otherwise.
160    ///
161    /// ```
162    /// # use ciborium::value::Value;
163    /// #
164    /// let value = Value::Text(String::from("hello"));
165    ///
166    /// assert!(value.is_text());
167    /// ```
168    pub fn is_text(&self) -> bool {
169        self.as_text().is_some()
170    }
171
172    /// If the `Value` is a `Text`, returns a reference to the associated `String` data.
173    /// Returns None otherwise.
174    ///
175    /// ```
176    /// # use ciborium::value::Value;
177    /// #
178    /// let value = Value::Text(String::from("hello"));
179    ///
180    /// // We can read the String
181    /// assert_eq!(value.as_text().unwrap(), "hello");
182    /// ```
183    pub fn as_text(&self) -> Option<&str> {
184        match *self {
185            Value::Text(ref s) => Some(s),
186            _ => None,
187        }
188    }
189
190    /// If the `Value` is a `Text`, returns a mutable reference to the associated `String` data.
191    /// Returns None otherwise.
192    ///
193    /// ```
194    /// # use ciborium::value::Value;
195    /// #
196    /// let mut value = Value::Text(String::from("hello"));
197    /// value.as_text_mut().unwrap().clear();
198    ///
199    /// assert_eq!(value.as_text().unwrap(), &String::from(""));
200    /// ```
201    pub fn as_text_mut(&mut self) -> Option<&mut String> {
202        match *self {
203            Value::Text(ref mut s) => Some(s),
204            _ => None,
205        }
206    }
207
208    /// Returns true if the `Value` is a `Bool`. Returns false otherwise.
209    ///
210    /// ```
211    /// # use ciborium::value::Value;
212    /// #
213    /// let value = Value::Bool(false);
214    ///
215    /// assert!(value.is_bool());
216    /// ```
217    pub fn is_bool(&self) -> bool {
218        self.as_bool().is_some()
219    }
220
221    /// If the `Value` is a `Bool`, returns a copy of the associated boolean value. Returns None
222    /// otherwise.
223    ///
224    /// ```
225    /// # use ciborium::value::Value;
226    /// #
227    /// let value = Value::Bool(false);
228    ///
229    /// assert_eq!(value.as_bool().unwrap(), false);
230    /// ```
231    pub fn as_bool(&self) -> Option<bool> {
232        match *self {
233            Value::Bool(b) => Some(b),
234            _ => None,
235        }
236    }
237
238    /// Returns true if the `Value` is a `Null`. Returns false otherwise.
239    ///
240    /// ```
241    /// # use ciborium::value::Value;
242    /// #
243    /// let value = Value::Null;
244    ///
245    /// assert!(value.is_null());
246    /// ```
247    pub fn is_null(&self) -> bool {
248        matches!(self, Value::Null)
249    }
250
251    /// Returns true if the `Value` is a `Tag`. Returns false otherwise.
252    ///
253    /// ```
254    /// # use ciborium::value::Value;
255    /// #
256    /// let value = Value::Tag(61, Box::from(Value::Null));
257    ///
258    /// assert!(value.is_tag());
259    /// ```
260    pub fn is_tag(&self) -> bool {
261        self.as_tag().is_some()
262    }
263
264    /// If the `Value` is a `Tag`, returns the associated tag value and a reference to the tag `Value`.
265    /// Returns None otherwise.
266    ///
267    /// ```
268    /// # use ciborium::value::Value;
269    /// #
270    /// let value = Value::Tag(61, Box::from(Value::Bytes(vec![104, 101, 108, 108, 111])));
271    ///
272    /// let (tag, data) = value.as_tag().unwrap();
273    /// assert_eq!(tag, 61);
274    /// assert_eq!(data, &Value::Bytes(vec![104, 101, 108, 108, 111]));
275    /// ```
276    pub fn as_tag(&self) -> Option<(u64, &Value)> {
277        match self {
278            Value::Tag(tag, data) => Some((*tag, data)),
279            _ => None,
280        }
281    }
282
283    /// If the `Value` is a `Tag`, returns the associated tag value and a mutable reference
284    /// to the tag `Value`. Returns None otherwise.
285    ///
286    /// ```
287    /// # use ciborium::value::Value;
288    /// #
289    /// let mut value = Value::Tag(61, Box::from(Value::Bytes(vec![104, 101, 108, 108, 111])));
290    ///
291    /// let (tag, mut data) = value.as_tag_mut().unwrap();
292    /// data.as_bytes_mut().unwrap().clear();
293    /// assert_eq!(tag, &61);
294    /// assert_eq!(data, &Value::Bytes(vec![]));
295    /// ```
296    pub fn as_tag_mut(&mut self) -> Option<(&mut u64, &mut Value)> {
297        match self {
298            Value::Tag(tag, data) => Some((tag, data.as_mut())),
299            _ => None,
300        }
301    }
302
303    /// Returns true if the `Value` is an Array. Returns false otherwise.
304    ///
305    /// ```
306    /// # use ciborium::value::Value;
307    /// #
308    /// let value = Value::Array(
309    ///     vec![
310    ///         Value::Text(String::from("foo")),
311    ///         Value::Text(String::from("bar"))
312    ///     ]
313    /// );
314    ///
315    /// assert!(value.is_array());
316    /// ```
317    pub fn is_array(&self) -> bool {
318        self.as_array().is_some()
319    }
320
321    /// If the `Value` is an Array, returns a reference to the associated vector. Returns None
322    /// otherwise.
323    ///
324    /// ```
325    /// # use ciborium::value::Value;
326    /// #
327    /// let value = Value::Array(
328    ///     vec![
329    ///         Value::Text(String::from("foo")),
330    ///         Value::Text(String::from("bar"))
331    ///     ]
332    /// );
333    ///
334    /// // The length of `value` is 2 elements.
335    /// assert_eq!(value.as_array().unwrap().len(), 2);
336    /// ```
337    pub fn as_array(&self) -> Option<&Vec<Value>> {
338        match *self {
339            Value::Array(ref array) => Some(&*array),
340            _ => None,
341        }
342    }
343
344    /// If the `Value` is an Array, returns a mutable reference to the associated vector.
345    /// Returns None otherwise.
346    ///
347    /// ```
348    /// # use ciborium::value::Value;
349    /// #
350    /// let mut value = Value::Array(
351    ///     vec![
352    ///         Value::Text(String::from("foo")),
353    ///         Value::Text(String::from("bar"))
354    ///     ]
355    /// );
356    ///
357    /// value.as_array_mut().unwrap().clear();
358    /// assert_eq!(value, Value::Array(vec![]));
359    /// ```
360    pub fn as_array_mut(&mut self) -> Option<&mut Vec<Value>> {
361        match *self {
362            Value::Array(ref mut list) => Some(list),
363            _ => None,
364        }
365    }
366
367    /// Returns true if the `Value` is a Map. Returns false otherwise.
368    ///
369    /// ```
370    /// # use ciborium::value::Value;
371    /// #
372    /// let value = Value::Map(
373    ///     vec![
374    ///         (Value::Text(String::from("foo")), Value::Text(String::from("bar")))
375    ///     ]
376    /// );
377    ///
378    /// assert!(value.is_map());
379    /// ```
380    pub fn is_map(&self) -> bool {
381        self.as_map().is_some()
382    }
383
384    /// If the `Value` is a Map, returns a reference to the associated Map data. Returns None
385    /// otherwise.
386    ///
387    /// ```
388    /// # use ciborium::value::Value;
389    /// #
390    /// let value = Value::Map(
391    ///     vec![
392    ///         (Value::Text(String::from("foo")), Value::Text(String::from("bar")))
393    ///     ]
394    /// );
395    ///
396    /// // The length of data is 1 entry (1 key/value pair).
397    /// assert_eq!(value.as_map().unwrap().len(), 1);
398    ///
399    /// // The content of the first element is what we expect
400    /// assert_eq!(
401    ///     value.as_map().unwrap().get(0).unwrap(),
402    ///     &(Value::Text(String::from("foo")), Value::Text(String::from("bar")))
403    /// );
404    /// ```
405    pub fn as_map(&self) -> Option<&Vec<(Value, Value)>> {
406        match *self {
407            Value::Map(ref map) => Some(map),
408            _ => None,
409        }
410    }
411
412    /// If the `Value` is a Map, returns a mutable reference to the associated Map Data.
413    /// Returns None otherwise.
414    ///
415    /// ```
416    /// # use ciborium::value::Value;
417    /// #
418    /// let mut value = Value::Map(
419    ///     vec![
420    ///         (Value::Text(String::from("foo")), Value::Text(String::from("bar")))
421    ///     ]
422    /// );
423    ///
424    /// value.as_map_mut().unwrap().clear();
425    /// assert_eq!(value, Value::Map(vec![]));
426    /// assert_eq!(value.as_map().unwrap().len(), 0);
427    /// ```
428    pub fn as_map_mut(&mut self) -> Option<&mut Vec<(Value, Value)>> {
429        match *self {
430            Value::Map(ref mut map) => Some(map),
431            _ => None,
432        }
433    }
434}
435
436macro_rules! implfrom {
437    ($($v:ident($t:ty)),+ $(,)?) => {
438        $(
439            impl From<$t> for Value {
440                #[inline]
441                fn from(value: $t) -> Self {
442                    Self::$v(value.into())
443                }
444            }
445        )+
446    };
447}
448
449implfrom! {
450    Integer(Integer),
451    Integer(u64),
452    Integer(i64),
453    Integer(u32),
454    Integer(i32),
455    Integer(u16),
456    Integer(i16),
457    Integer(u8),
458    Integer(i8),
459
460    Bytes(Vec<u8>),
461    Bytes(&[u8]),
462
463    Float(f64),
464    Float(f32),
465
466    Text(String),
467    Text(&str),
468
469    Bool(bool),
470
471    Array(&[Value]),
472    Array(Vec<Value>),
473
474    Map(&[(Value, Value)]),
475    Map(Vec<(Value, Value)>),
476}
477
478impl From<u128> for Value {
479    #[inline]
480    fn from(value: u128) -> Self {
481        if let Ok(x) = Integer::try_from(value) {
482            return Value::Integer(x);
483        }
484
485        let mut bytes = &value.to_be_bytes()[..];
486        while let Some(0) = bytes.get(0) {
487            bytes = &bytes[1..];
488        }
489
490        Value::Tag(ciborium_ll::tag::BIGPOS, Value::Bytes(bytes.into()).into())
491    }
492}
493
494impl From<i128> for Value {
495    #[inline]
496    fn from(value: i128) -> Self {
497        if let Ok(x) = Integer::try_from(value) {
498            return Value::Integer(x);
499        }
500
501        let (tag, raw) = match value.is_negative() {
502            true => (ciborium_ll::tag::BIGNEG, value as u128 ^ !0),
503            false => (ciborium_ll::tag::BIGPOS, value as u128),
504        };
505
506        let mut bytes = &raw.to_be_bytes()[..];
507        while let Some(0) = bytes.get(0) {
508            bytes = &bytes[1..];
509        }
510
511        Value::Tag(tag, Value::Bytes(bytes.into()).into())
512    }
513}
514
515impl From<char> for Value {
516    #[inline]
517    fn from(value: char) -> Self {
518        let mut v = String::with_capacity(1);
519        v.push(value);
520        Value::Text(v)
521    }
522}