Skip to main content

neco_cbor/
value.rs

1use alloc::boxed::Box;
2use alloc::string::String;
3use alloc::vec::Vec;
4
5use crate::error::AccessError;
6
7#[derive(Debug, Clone, PartialEq, Eq)]
8pub enum CborValue {
9    Unsigned(u64),
10    Negative(i64),
11    Bytes(Vec<u8>),
12    Text(String),
13    Array(Vec<CborValue>),
14    Map(Vec<(CborValue, CborValue)>),
15    Tag(u64, Box<CborValue>),
16    Bool(bool),
17    Null,
18}
19
20impl CborValue {
21    pub fn as_unsigned(&self) -> Option<u64> {
22        match self {
23            Self::Unsigned(value) => Some(*value),
24            _ => None,
25        }
26    }
27
28    pub fn as_negative(&self) -> Option<i64> {
29        match self {
30            Self::Negative(value) => Some(*value),
31            _ => None,
32        }
33    }
34
35    pub fn as_bytes(&self) -> Option<&[u8]> {
36        match self {
37            Self::Bytes(value) => Some(value.as_slice()),
38            _ => None,
39        }
40    }
41
42    pub fn as_text(&self) -> Option<&str> {
43        match self {
44            Self::Text(value) => Some(value.as_str()),
45            _ => None,
46        }
47    }
48
49    pub fn as_array(&self) -> Option<&[CborValue]> {
50        match self {
51            Self::Array(values) => Some(values.as_slice()),
52            _ => None,
53        }
54    }
55
56    pub fn as_map(&self) -> Option<&[(CborValue, CborValue)]> {
57        match self {
58            Self::Map(values) => Some(values.as_slice()),
59            _ => None,
60        }
61    }
62
63    pub fn as_tag(&self) -> Option<(u64, &CborValue)> {
64        match self {
65            Self::Tag(tag, value) => Some((*tag, value.as_ref())),
66            _ => None,
67        }
68    }
69
70    pub fn as_bool(&self) -> Option<bool> {
71        match self {
72            Self::Bool(value) => Some(*value),
73            _ => None,
74        }
75    }
76
77    pub fn is_null(&self) -> bool {
78        matches!(self, Self::Null)
79    }
80
81    pub fn get(&self, key: &str) -> Option<&CborValue> {
82        match self {
83            Self::Map(entries) => entries
84                .iter()
85                .find_map(|(entry_key, value)| match entry_key {
86                    Self::Text(text) if text == key => Some(value),
87                    _ => None,
88                }),
89            _ => None,
90        }
91    }
92
93    pub fn required_text(&self, key: &str) -> Result<&str, AccessError> {
94        self.required_value(key, CborValue::as_text, "text")
95    }
96
97    pub fn required_bytes(&self, key: &str) -> Result<&[u8], AccessError> {
98        self.required_value(key, CborValue::as_bytes, "bytes")
99    }
100
101    pub fn required_unsigned(&self, key: &str) -> Result<u64, AccessError> {
102        self.required_value(key, CborValue::as_unsigned, "unsigned")
103    }
104
105    pub fn required_negative(&self, key: &str) -> Result<i64, AccessError> {
106        self.required_value(key, CborValue::as_negative, "negative")
107    }
108
109    pub fn required_bool(&self, key: &str) -> Result<bool, AccessError> {
110        self.required_value(key, CborValue::as_bool, "bool")
111    }
112
113    pub fn required_array(&self, key: &str) -> Result<&[CborValue], AccessError> {
114        self.required_value(key, CborValue::as_array, "array")
115    }
116
117    pub fn required_map(&self, key: &str) -> Result<&[(CborValue, CborValue)], AccessError> {
118        self.required_value(key, CborValue::as_map, "map")
119    }
120
121    pub fn required_tag(&self, key: &str) -> Result<(u64, &CborValue), AccessError> {
122        self.required_value(key, CborValue::as_tag, "tag")
123    }
124
125    fn required_value<'a, T>(
126        &'a self,
127        key: &str,
128        accessor: impl FnOnce(&'a CborValue) -> Option<T>,
129        expected: &'static str,
130    ) -> Result<T, AccessError> {
131        let value = self.object_field(key)?;
132        accessor(value).ok_or_else(|| AccessError::TypeMismatch {
133            field: key.into(),
134            expected,
135        })
136    }
137
138    fn object_field(&self, key: &str) -> Result<&CborValue, AccessError> {
139        match self {
140            Self::Map(_) => self
141                .get(key)
142                .ok_or_else(|| AccessError::MissingField(key.into())),
143            _ => Err(AccessError::NotAMap),
144        }
145    }
146}