cbor_data/validated/
item.rs

1use super::iterators::{ArrayIter, BytesIter, DictIter, StringIter};
2use crate::{constants::TAG_CBOR_ITEM, Cbor, CborValue, DebugUsingDisplay, Tags};
3use std::fmt::{Debug, Display, Formatter, Write};
4
5/// Low-level encoding of a CBOR item. Use [`CborValue`](value/enum.CborValue.html) for inspecting values.
6///
7/// You can obtain this representation from [`Cbor::kind`](struct.Cbor.html#method.kind) or
8/// [`TaggedItem::kind`](struct.TaggedItem.html#method.kind).
9///
10/// Beware of the `Neg` variant, which carries `-1 - x`.
11#[derive(PartialEq, PartialOrd, Clone, Copy)]
12pub enum ItemKind<'a> {
13    Pos(u64),
14    Neg(u64),
15    Float(f64),
16    Str(StringIter<'a>),
17    Bytes(BytesIter<'a>),
18    Bool(bool),
19    Null,
20    Undefined,
21    Simple(u8),
22    Array(ArrayIter<'a>),
23    Dict(DictIter<'a>),
24}
25
26impl<'a> Debug for ItemKind<'a> {
27    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
28        match self {
29            Self::Pos(arg0) => f.debug_tuple("Pos").field(arg0).finish(),
30            Self::Neg(arg0) => f.debug_tuple("Neg").field(arg0).finish(),
31            Self::Float(arg0) => f.debug_tuple("Float").field(arg0).finish(),
32            Self::Str(arg0) => f
33                .debug_tuple("Str")
34                .field(&DebugUsingDisplay(arg0))
35                .finish(),
36            Self::Bytes(arg0) => f
37                .debug_tuple("Bytes")
38                .field(&DebugUsingDisplay(arg0))
39                .finish(),
40            Self::Bool(arg0) => f.debug_tuple("Bool").field(arg0).finish(),
41            Self::Null => write!(f, "Null"),
42            Self::Undefined => write!(f, "Undefined"),
43            Self::Simple(arg0) => f.debug_tuple("Simple").field(arg0).finish(),
44            Self::Array(arg0) => write!(f, "Array({:?})", arg0.size()),
45            Self::Dict(arg0) => write!(f, "Dict({:?})", arg0.size()),
46        }
47    }
48}
49
50impl<'a> Display for ItemKind<'a> {
51    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
52        match self {
53            ItemKind::Pos(_) => write!(f, "positive number"),
54            ItemKind::Neg(_) => write!(f, "negative number"),
55            ItemKind::Float(_) => write!(f, "floating-point number"),
56            ItemKind::Str(_) => write!(f, "text string"),
57            ItemKind::Bytes(_) => write!(f, "byte string"),
58            ItemKind::Bool(_) => write!(f, "boolean"),
59            ItemKind::Null => write!(f, "null"),
60            ItemKind::Undefined => write!(f, "undefined"),
61            ItemKind::Simple(_) => write!(f, "simple value"),
62            ItemKind::Array(_) => write!(f, "array"),
63            ItemKind::Dict(_) => write!(f, "dictionary"),
64        }
65    }
66}
67
68impl<'a> ItemKind<'a> {
69    pub fn new(cbor: &'a Cbor) -> Self {
70        super::item(cbor.as_slice())
71    }
72}
73
74#[derive(Debug, Clone, Copy, PartialEq, Eq)]
75pub enum ItemKindShort {
76    Pos,
77    Neg,
78    Float,
79    Str,
80    Bytes,
81    Bool,
82    Null,
83    Undefined,
84    Simple,
85    Array,
86    Dict,
87}
88
89impl Display for ItemKindShort {
90    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
91        match self {
92            ItemKindShort::Pos => write!(f, "positive number"),
93            ItemKindShort::Neg => write!(f, "negative number"),
94            ItemKindShort::Float => write!(f, "floating-point number"),
95            ItemKindShort::Str => write!(f, "text string"),
96            ItemKindShort::Bytes => write!(f, "byte string"),
97            ItemKindShort::Bool => write!(f, "boolean"),
98            ItemKindShort::Null => write!(f, "null"),
99            ItemKindShort::Undefined => write!(f, "undefined"),
100            ItemKindShort::Simple => write!(f, "simple value"),
101            ItemKindShort::Array => write!(f, "array"),
102            ItemKindShort::Dict => write!(f, "dictionary"),
103        }
104    }
105}
106
107impl<'a> From<ItemKind<'a>> for ItemKindShort {
108    fn from(kind: ItemKind<'a>) -> Self {
109        match kind {
110            ItemKind::Pos(_) => ItemKindShort::Pos,
111            ItemKind::Neg(_) => ItemKindShort::Neg,
112            ItemKind::Float(_) => ItemKindShort::Float,
113            ItemKind::Str(_) => ItemKindShort::Str,
114            ItemKind::Bytes(_) => ItemKindShort::Bytes,
115            ItemKind::Bool(_) => ItemKindShort::Bool,
116            ItemKind::Null => ItemKindShort::Null,
117            ItemKind::Undefined => ItemKindShort::Undefined,
118            ItemKind::Simple(_) => ItemKindShort::Simple,
119            ItemKind::Array(_) => ItemKindShort::Array,
120            ItemKind::Dict(_) => ItemKindShort::Dict,
121        }
122    }
123}
124
125/// Representation of a possibly tagged CBOR data item
126///
127/// You can obtain this representation using [`Cbor::tagged_item`](struct.Cbor.html#method.tagged_item).
128///
129/// It holds an iterable representation of the tags, a decoded [`ItemKind`](enum.ItemKind.html)
130/// and a reference to the underlying bytes for the whole item. Since all these are shallow
131/// references to existing data, this structure itself is `Copy`.
132#[derive(Clone, Copy, PartialEq)]
133pub struct TaggedItem<'a> {
134    tags: Tags<'a>,
135    kind: ItemKind<'a>,
136    cbor: &'a Cbor,
137}
138
139impl<'a> Debug for TaggedItem<'a> {
140    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
141        write!(f, "TaggedItem({}, {:?})", self.tags, self.kind)
142    }
143}
144
145struct W([u8; 32], u8);
146impl W {
147    pub fn new() -> Self {
148        Self([0; 32], 0)
149    }
150    pub fn as_str(&self) -> &str {
151        unsafe { std::str::from_utf8_unchecked(&self.0.as_ref()[0..self.1 as usize]) }
152    }
153}
154impl std::fmt::Write for W {
155    fn write_str(&mut self, s: &str) -> std::fmt::Result {
156        let end = self.1 as usize + s.len();
157        if end > 24 {
158            Err(std::fmt::Error)
159        } else {
160            self.0.as_mut()[self.1 as usize..end].copy_from_slice(s.as_bytes());
161            self.1 = end as u8;
162            Ok(())
163        }
164    }
165}
166
167#[allow(clippy::many_single_char_names)]
168fn write_float(f: &mut std::fmt::Formatter<'_>, x: f64) -> std::fmt::Result {
169    if x == f64::INFINITY {
170        write!(f, "Infinity")
171    } else if x == f64::NEG_INFINITY {
172        write!(f, "-Infinity")
173    } else if x.is_nan() {
174        write!(f, "NaN")
175    } else {
176        let mut w = W::new();
177        if x != 0.0 && (x.abs() < 1e-6 || x.abs() > 1e16) {
178            write!(w, "{:e}", x)?;
179        } else {
180            write!(w, "{}", x)?;
181        }
182        let s = w.as_str();
183
184        let e = s.find('e').unwrap_or(s.len());
185        let (mantissa, exponent) = s.split_at(e);
186        write!(f, "{}", mantissa)?;
187        if !mantissa.contains('.') {
188            write!(f, ".0")?;
189        }
190        write!(f, "{}", exponent)?;
191        Ok(())
192    }
193}
194
195impl<'a> Display for TaggedItem<'a> {
196    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
197        if let (Some(TAG_CBOR_ITEM), ItemKind::Bytes(bytes)) = (self.tags().single(), self.kind()) {
198            let indefinite = if bytes.is_indefinite() { "_ " } else { "" };
199            let bytes = bytes.as_cow();
200            let cbor = Cbor::unchecked(bytes.as_ref());
201            return write!(f, "<{}{}>", indefinite, cbor);
202        }
203
204        let mut parens = 0;
205        for tag in self.tags() {
206            write!(f, "{}(", tag)?;
207            parens += 1;
208        }
209        match self.kind() {
210            ItemKind::Pos(x) => write!(f, "{}", x)?,
211            ItemKind::Neg(x) => write!(f, "{}", -1 - (i128::from(x)))?,
212            ItemKind::Float(x) => write_float(f, x)?,
213            ItemKind::Str(mut s) => {
214                if s.is_indefinite() {
215                    if s.is_empty() {
216                        write!(f, "\"\"_")?;
217                    } else {
218                        write!(f, "(_")?;
219                        let mut first = true;
220                        for s in s {
221                            if first {
222                                first = false;
223                            } else {
224                                write!(f, ",")?;
225                            }
226                            write!(f, " \"{}\"", s.escape_debug())?;
227                        }
228                        write!(f, ")")?;
229                    }
230                } else {
231                    let s = s.next().unwrap();
232                    write!(f, "\"{}\"", s.escape_debug())?;
233                }
234            }
235            ItemKind::Bytes(mut b) => {
236                if b.is_indefinite() {
237                    if b.is_empty() {
238                        write!(f, "''_")?;
239                    } else {
240                        write!(f, "(_")?;
241                        let mut first = true;
242                        for b in b {
243                            if first {
244                                first = false;
245                            } else {
246                                write!(f, ",")?;
247                            }
248                            write!(f, " h'")?;
249                            for byte in b {
250                                write!(f, "{:02x}", byte)?;
251                            }
252                            write!(f, "'")?;
253                        }
254                        write!(f, ")")?;
255                    }
256                } else {
257                    let b = b.next().unwrap();
258                    write!(f, "h'")?;
259                    for byte in b {
260                        write!(f, "{:02x}", byte)?;
261                    }
262                    write!(f, "'")?;
263                }
264            }
265            ItemKind::Bool(b) => write!(f, "{}", b)?,
266            ItemKind::Null => write!(f, "null")?,
267            ItemKind::Undefined => write!(f, "undefined")?,
268            ItemKind::Simple(s) => write!(f, "simple({})", s)?,
269            ItemKind::Array(_) => unreachable!(),
270            ItemKind::Dict(_) => unreachable!(),
271        }
272        for _ in 0..parens {
273            write!(f, ")")?;
274        }
275        Ok(())
276    }
277}
278
279impl<'a> TaggedItem<'a> {
280    pub fn new(cbor: &'a Cbor) -> Self {
281        let (tags, kind) = super::tagged_item(cbor.as_ref());
282        Self { tags, kind, cbor }
283    }
284
285    /// Interpret the CBOR item at a higher level
286    ///
287    /// While [`kind`](#method.kind) gives you precise information on how the item is encoded,
288    /// this method interprets the tag-based encoding according to the standard, adding for example
289    /// big integers, decimals, and floats, or turning base64-encoded text strings into binary strings.
290    pub fn decode(self) -> CborValue<'a> {
291        CborValue::new(self)
292    }
293
294    /// An iterator over the tags of this item
295    pub fn tags(&self) -> Tags<'a> {
296        self.tags
297    }
298
299    /// A decoded form of the low-level representation of the CBOR item
300    pub fn kind(&self) -> ItemKind<'a> {
301        self.kind
302    }
303
304    /// A reference to the underlying bytes from which this structure has been lifted
305    pub fn cbor(&self) -> &'a Cbor {
306        self.cbor
307    }
308}