Skip to main content

tinycbor/
tag.rs

1//! Tagged values.
2use core::ops::Deref;
3
4use embedded_io::Write;
5
6use crate::{CborLen, Decode, Encode, Encoder, InvalidHeader, TAGGED, primitive, type_of};
7
8/// Possible errors when decoding a tagged value.
9#[derive(#[automatically_derived]
impl<E: ::core::fmt::Debug> ::core::fmt::Debug for Error<E> {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        match self {
            Error::Malformed(__self_0) =>
                ::core::fmt::Formatter::debug_tuple_field1_finish(f,
                    "Malformed", &__self_0),
            Error::InvalidTag =>
                ::core::fmt::Formatter::write_str(f, "InvalidTag"),
            Error::Inner(__self_0) =>
                ::core::fmt::Formatter::debug_tuple_field1_finish(f, "Inner",
                    &__self_0),
        }
    }
}Debug, #[automatically_derived]
impl<E: ::core::clone::Clone> ::core::clone::Clone for Error<E> {
    #[inline]
    fn clone(&self) -> Error<E> {
        match self {
            Error::Malformed(__self_0) =>
                Error::Malformed(::core::clone::Clone::clone(__self_0)),
            Error::InvalidTag => Error::InvalidTag,
            Error::Inner(__self_0) =>
                Error::Inner(::core::clone::Clone::clone(__self_0)),
        }
    }
}Clone, #[automatically_derived]
impl<E: ::core::marker::Copy> ::core::marker::Copy for Error<E> { }Copy, #[automatically_derived]
impl<E: ::core::cmp::PartialEq> ::core::cmp::PartialEq for Error<E> {
    #[inline]
    fn eq(&self, other: &Error<E>) -> bool {
        let __self_discr = ::core::intrinsics::discriminant_value(self);
        let __arg1_discr = ::core::intrinsics::discriminant_value(other);
        __self_discr == __arg1_discr &&
            match (self, other) {
                (Error::Malformed(__self_0), Error::Malformed(__arg1_0)) =>
                    __self_0 == __arg1_0,
                (Error::Inner(__self_0), Error::Inner(__arg1_0)) =>
                    __self_0 == __arg1_0,
                _ => true,
            }
    }
}PartialEq, #[automatically_derived]
impl<E: ::core::cmp::Eq> ::core::cmp::Eq for Error<E> {
    #[inline]
    #[doc(hidden)]
    #[coverage(off)]
    fn assert_receiver_is_total_eq(&self) {
        let _: ::core::cmp::AssertParamIsEq<primitive::Error>;
        let _: ::core::cmp::AssertParamIsEq<E>;
    }
}Eq)]
10pub enum Error<E> {
11    /// Tag is malformed.
12    Malformed(primitive::Error),
13    /// Tag does not match the expected tag.
14    InvalidTag,
15    /// Error decoding the tagged value.
16    Inner(E),
17}
18
19impl<E> Error<E> {
20    /// Map a function on the inner error.
21    pub fn map<O>(self, f: impl FnOnce(E) -> O) -> Error<O> {
22        match self {
23            Error::Malformed(e) => Error::Malformed(e),
24            Error::InvalidTag => Error::InvalidTag,
25            Error::Inner(e) => Error::Inner(f(e)),
26        }
27    }
28}
29
30impl<E: core::fmt::Display> core::fmt::Display for Error<E> {
31    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
32        match self {
33            Error::Malformed(_) => f.write_fmt(format_args!("malformed tag"))write!(f, "malformed tag"),
34            Error::InvalidTag => f.write_fmt(format_args!("invalid tag"))write!(f, "invalid tag"),
35            Error::Inner(_) => f.write_fmt(format_args!("tagged value error"))write!(f, "tagged value error"),
36        }
37    }
38}
39
40impl<E> From<primitive::Error> for Error<E> {
41    fn from(e: primitive::Error) -> Self {
42        Error::Malformed(e)
43    }
44}
45
46impl<E> From<crate::EndOfInput> for Error<E> {
47    fn from(_: crate::EndOfInput) -> Self {
48        Error::Malformed(primitive::Error::EndOfInput)
49    }
50}
51
52impl<E> From<crate::InvalidHeader> for Error<E> {
53    fn from(_: crate::InvalidHeader) -> Self {
54        Error::Malformed(primitive::Error::InvalidHeader)
55    }
56}
57
58impl<E: core::error::Error + 'static> core::error::Error for Error<E> {
59    fn source(&self) -> Option<&(dyn core::error::Error + 'static)> {
60        match self {
61            Error::Malformed(e) => Some(e),
62            Error::InvalidTag => None,
63            Error::Inner(e) => Some(e),
64        }
65    }
66}
67
68/// Statically tag a value.
69///
70/// # Example
71///
72/// ```
73/// use tinycbor::{tag::{Tagged, IanaTag}, Decode, Decoder};
74///
75/// let input = [
76///     0xc0, 0x74, 0x32, 0x30, 0x31, 0x33, 0x2d, 0x30,
77///     0x33, 0x2d, 0x32, 0x31, 0x54, 0x32, 0x30, 0x3a,
78///     0x30, 0x34, 0x3a, 0x30, 0x30, 0x5a
79/// ];
80///
81/// let date_time: Tagged<&str, {IanaTag::DateTime as u64}> = Decode::decode(&mut Decoder(&input))?;
82/// assert_eq!(date_time.0, "2013-03-21T20:04:00Z");
83/// # Ok::<_, Box<dyn core::error::Error>>(())
84/// ```
85#[derive(#[automatically_derived]
impl<T: ::core::fmt::Debug + ?Sized, const N : u64> ::core::fmt::Debug for
    Tagged<T, N> {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        ::core::fmt::Formatter::debug_tuple_field1_finish(f, "Tagged",
            &&self.0)
    }
}Debug, #[automatically_derived]
impl<T: ::core::marker::Copy + ?Sized, const N : u64> ::core::marker::Copy for
    Tagged<T, N> {
}Copy, #[automatically_derived]
impl<T: ::core::clone::Clone + ?Sized, const N : u64> ::core::clone::Clone for
    Tagged<T, N> {
    #[inline]
    fn clone(&self) -> Tagged<T, N> {
        Tagged(::core::clone::Clone::clone(&self.0))
    }
}Clone, #[automatically_derived]
impl<T: ::core::default::Default + ?Sized, const N : u64>
    ::core::default::Default for Tagged<T, N> {
    #[inline]
    fn default() -> Tagged<T, N> {
        Tagged(::core::default::Default::default())
    }
}Default, #[automatically_derived]
impl<T: ::core::cmp::PartialEq + ?Sized, const N : u64> ::core::cmp::PartialEq
    for Tagged<T, N> {
    #[inline]
    fn eq(&self, other: &Tagged<T, N>) -> bool { self.0 == other.0 }
}PartialEq, #[automatically_derived]
impl<T: ::core::cmp::Eq + ?Sized, const N : u64> ::core::cmp::Eq for
    Tagged<T, N> {
    #[inline]
    #[doc(hidden)]
    #[coverage(off)]
    fn assert_receiver_is_total_eq(&self) {
        let _: ::core::cmp::AssertParamIsEq<T>;
    }
}Eq, #[automatically_derived]
impl<T: ::core::cmp::PartialOrd + ?Sized, const N : u64>
    ::core::cmp::PartialOrd for Tagged<T, N> {
    #[inline]
    fn partial_cmp(&self, other: &Tagged<T, N>)
        -> ::core::option::Option<::core::cmp::Ordering> {
        ::core::cmp::PartialOrd::partial_cmp(&self.0, &other.0)
    }
}PartialOrd, #[automatically_derived]
impl<T: ::core::cmp::Ord + ?Sized, const N : u64> ::core::cmp::Ord for
    Tagged<T, N> {
    #[inline]
    fn cmp(&self, other: &Tagged<T, N>) -> ::core::cmp::Ordering {
        ::core::cmp::Ord::cmp(&self.0, &other.0)
    }
}Ord, #[automatically_derived]
impl<T: ::core::hash::Hash + ?Sized, const N : u64> ::core::hash::Hash for
    Tagged<T, N> {
    #[inline]
    fn hash<__H: ::core::hash::Hasher>(&self, state: &mut __H) {
        ::core::hash::Hash::hash(&self.0, state)
    }
}Hash)]
86#[repr(transparent)]
87pub struct Tagged<T: ?Sized, const N: u64>(pub T);
88
89impl<T, const N: u64> Tagged<T, N> {
90    /// Unwrap the tagged value.
91    pub fn into(self) -> T {
92        self.0
93    }
94}
95
96impl<T, const N: u64> From<Tagged<T, N>> for Option<T> {
97    fn from(val: Tagged<T, N>) -> Self {
98        Some(val.0)
99    }
100}
101
102impl<T, const N: u64> From<T> for Tagged<T, N> {
103    fn from(val: T) -> Self {
104        Self(val)
105    }
106}
107
108impl<'a, T: ?Sized, const N: u64> From<&'a T> for &'a Tagged<T, N> {
109    fn from(val: &T) -> Self {
110        // SAFETY: Tagged is repr(transparent)
111        unsafe { &*(val as *const T as *const Tagged<T, N>) }
112    }
113}
114
115impl<T: ?Sized, const N: u64> AsRef<T> for Tagged<T, N> {
116    fn as_ref(&self) -> &T {
117        &self.0
118    }
119}
120
121impl<T: ?Sized, const N: u64> AsMut<T> for Tagged<T, N> {
122    fn as_mut(&mut self) -> &mut T {
123        &mut self.0
124    }
125}
126
127impl<T: ?Sized, const N: u64> Deref for Tagged<T, N> {
128    type Target = T;
129
130    fn deref(&self) -> &Self::Target {
131        &self.0
132    }
133}
134
135impl<T: ?Sized, const N: u64> core::ops::DerefMut for Tagged<T, N> {
136    fn deref_mut(&mut self) -> &mut Self::Target {
137        &mut self.0
138    }
139}
140
141impl<'a, T, const N: u64> Decode<'a> for Tagged<T, N>
142where
143    T: Decode<'a>,
144{
145    type Error = Error<T::Error>;
146
147    fn decode(d: &mut crate::Decoder<'a>) -> Result<Self, Self::Error> {
148        let b = d.peek()?;
149        if TAGGED != type_of(b) {
150            return Err(InvalidHeader.into());
151        }
152        if d.unsigned().map_err(Error::Malformed)? != N {
153            return Err(Error::InvalidTag);
154        }
155
156        T::decode(d).map(Tagged::<T, N>).map_err(Error::Inner)
157    }
158}
159
160impl<const N: u64, T: Encode + ?Sized> Encode for Tagged<T, N> {
161    fn encode<W: Write>(&self, e: &mut Encoder<W>) -> Result<(), W::Error> {
162        e.type_len(TAGGED, N)?;
163        self.0.encode(e)
164    }
165}
166
167impl<const N: u64, T: CborLen + ?Sized> CborLen for Tagged<T, N> {
168    fn cbor_len(&self) -> usize {
169        N.cbor_len() + self.0.cbor_len()
170    }
171}
172
173/// IANA registered tags.
174///
175/// See <https://www.iana.org/assignments/cbor-tags/cbor-tags.xhtml> for details.
176#[derive(#[automatically_derived]
impl ::core::clone::Clone for IanaTag {
    #[inline]
    fn clone(&self) -> IanaTag { *self }
}Clone, #[automatically_derived]
impl ::core::marker::Copy for IanaTag { }Copy, #[automatically_derived]
impl ::core::cmp::PartialEq for IanaTag {
    #[inline]
    fn eq(&self, other: &IanaTag) -> bool {
        let __self_discr = ::core::intrinsics::discriminant_value(self);
        let __arg1_discr = ::core::intrinsics::discriminant_value(other);
        __self_discr == __arg1_discr
    }
}PartialEq, #[automatically_derived]
impl ::core::cmp::PartialOrd for IanaTag {
    #[inline]
    fn partial_cmp(&self, other: &IanaTag)
        -> ::core::option::Option<::core::cmp::Ordering> {
        let __self_discr = ::core::intrinsics::discriminant_value(self);
        let __arg1_discr = ::core::intrinsics::discriminant_value(other);
        ::core::cmp::PartialOrd::partial_cmp(&__self_discr, &__arg1_discr)
    }
}PartialOrd, #[automatically_derived]
impl ::core::cmp::Eq for IanaTag {
    #[inline]
    #[doc(hidden)]
    #[coverage(off)]
    fn assert_receiver_is_total_eq(&self) {}
}Eq, #[automatically_derived]
impl ::core::cmp::Ord for IanaTag {
    #[inline]
    fn cmp(&self, other: &IanaTag) -> ::core::cmp::Ordering {
        let __self_discr = ::core::intrinsics::discriminant_value(self);
        let __arg1_discr = ::core::intrinsics::discriminant_value(other);
        ::core::cmp::Ord::cmp(&__self_discr, &__arg1_discr)
    }
}Ord, #[automatically_derived]
impl ::core::fmt::Debug for IanaTag {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        ::core::fmt::Formatter::write_str(f,
            match self {
                IanaTag::DateTime => "DateTime",
                IanaTag::Timestamp => "Timestamp",
                IanaTag::PosBignum => "PosBignum",
                IanaTag::NegBignum => "NegBignum",
                IanaTag::Decimal => "Decimal",
                IanaTag::Bigfloat => "Bigfloat",
                IanaTag::ToBase64Url => "ToBase64Url",
                IanaTag::ToBase64 => "ToBase64",
                IanaTag::ToBase16 => "ToBase16",
                IanaTag::Cbor => "Cbor",
                IanaTag::Uri => "Uri",
                IanaTag::Base64Url => "Base64Url",
                IanaTag::Base64 => "Base64",
                IanaTag::Regex => "Regex",
                IanaTag::Mime => "Mime",
                IanaTag::MultiDimArrayR => "MultiDimArrayR",
                IanaTag::HomogenousArray => "HomogenousArray",
                IanaTag::TypedArrayU8 => "TypedArrayU8",
                IanaTag::TypedArrayU16B => "TypedArrayU16B",
                IanaTag::TypedArrayU32B => "TypedArrayU32B",
                IanaTag::TypedArrayU64B => "TypedArrayU64B",
                IanaTag::TypedArrayU8Clamped => "TypedArrayU8Clamped",
                IanaTag::TypedArrayU16L => "TypedArrayU16L",
                IanaTag::TypedArrayU32L => "TypedArrayU32L",
                IanaTag::TypedArrayU64L => "TypedArrayU64L",
                IanaTag::TypedArrayI8 => "TypedArrayI8",
                IanaTag::TypedArrayI16B => "TypedArrayI16B",
                IanaTag::TypedArrayI32B => "TypedArrayI32B",
                IanaTag::TypedArrayI64B => "TypedArrayI64B",
                IanaTag::TypedArrayI16L => "TypedArrayI16L",
                IanaTag::TypedArrayI32L => "TypedArrayI32L",
                IanaTag::TypedArrayI64L => "TypedArrayI64L",
                IanaTag::TypedArrayF16B => "TypedArrayF16B",
                IanaTag::TypedArrayF32B => "TypedArrayF32B",
                IanaTag::TypedArrayF64B => "TypedArrayF64B",
                IanaTag::TypedArrayF128B => "TypedArrayF128B",
                IanaTag::TypedArrayF16L => "TypedArrayF16L",
                IanaTag::TypedArrayF32L => "TypedArrayF32L",
                IanaTag::TypedArrayF64L => "TypedArrayF64L",
                IanaTag::TypedArrayF128L => "TypedArrayF128L",
                IanaTag::MultiDimArrayC => "MultiDimArrayC",
            })
    }
}Debug, #[automatically_derived]
impl ::core::hash::Hash for IanaTag {
    #[inline]
    fn hash<__H: ::core::hash::Hasher>(&self, state: &mut __H) {
        let __self_discr = ::core::intrinsics::discriminant_value(self);
        ::core::hash::Hash::hash(&__self_discr, state)
    }
}Hash)]
177#[repr(u64)]
178#[non_exhaustive]
179pub enum IanaTag {
180    DateTime = 0x00,
181    Timestamp = 0x01,
182    PosBignum = 0x02,
183    NegBignum = 0x03,
184    Decimal = 0x04,
185    Bigfloat = 0x05,
186    ToBase64Url = 0x15,
187    ToBase64 = 0x16,
188    ToBase16 = 0x17,
189    Cbor = 0x18,
190    Uri = 0x20,
191    Base64Url = 0x21,
192    Base64 = 0x22,
193    Regex = 0x23,
194    Mime = 0x24,
195    MultiDimArrayR = 0x28, // row-major order
196    // Typed arrays (RFC 8746):
197    HomogenousArray = 0x29,
198    TypedArrayU8 = 0x40,
199    TypedArrayU16B = 0x41,
200    TypedArrayU32B = 0x42,
201    TypedArrayU64B = 0x43,
202    TypedArrayU8Clamped = 0x44,
203    TypedArrayU16L = 0x45,
204    TypedArrayU32L = 0x46,
205    TypedArrayU64L = 0x47,
206    TypedArrayI8 = 0x48,
207    TypedArrayI16B = 0x49,
208    TypedArrayI32B = 0x4a,
209    TypedArrayI64B = 0x4b,
210    TypedArrayI16L = 0x4d,
211    TypedArrayI32L = 0x4e,
212    TypedArrayI64L = 0x4f,
213    TypedArrayF16B = 0x50,
214    TypedArrayF32B = 0x51,
215    TypedArrayF64B = 0x52,
216    TypedArrayF128B = 0x53,
217    TypedArrayF16L = 0x54,
218    TypedArrayF32L = 0x55,
219    TypedArrayF64L = 0x56,
220    TypedArrayF128L = 0x57,
221    MultiDimArrayC = 0x410, // column-major order
222}
223
224impl TryFrom<u64> for IanaTag {
225    type Error = Unknown;
226
227    fn try_from(t: u64) -> Result<Self, Self::Error> {
228        match t {
229            0x00 => Ok(Self::DateTime),
230            0x01 => Ok(Self::Timestamp),
231            0x02 => Ok(Self::PosBignum),
232            0x03 => Ok(Self::NegBignum),
233            0x04 => Ok(Self::Decimal),
234            0x05 => Ok(Self::Bigfloat),
235            0x15 => Ok(Self::ToBase64Url),
236            0x16 => Ok(Self::ToBase64),
237            0x17 => Ok(Self::ToBase16),
238            0x18 => Ok(Self::Cbor),
239            0x20 => Ok(Self::Uri),
240            0x21 => Ok(Self::Base64Url),
241            0x22 => Ok(Self::Base64),
242            0x23 => Ok(Self::Regex),
243            0x24 => Ok(Self::Mime),
244            0x28 => Ok(Self::MultiDimArrayR),
245            0x29 => Ok(Self::HomogenousArray),
246            0x40 => Ok(Self::TypedArrayU8),
247            0x41 => Ok(Self::TypedArrayU16B),
248            0x42 => Ok(Self::TypedArrayU32B),
249            0x43 => Ok(Self::TypedArrayU64B),
250            0x44 => Ok(Self::TypedArrayU8Clamped),
251            0x45 => Ok(Self::TypedArrayU16L),
252            0x46 => Ok(Self::TypedArrayU32L),
253            0x47 => Ok(Self::TypedArrayU64L),
254            0x48 => Ok(Self::TypedArrayI8),
255            0x49 => Ok(Self::TypedArrayI16B),
256            0x4a => Ok(Self::TypedArrayI32B),
257            0x4b => Ok(Self::TypedArrayI64B),
258            0x4d => Ok(Self::TypedArrayI16L),
259            0x4e => Ok(Self::TypedArrayI32L),
260            0x4f => Ok(Self::TypedArrayI64L),
261            0x50 => Ok(Self::TypedArrayF16B),
262            0x51 => Ok(Self::TypedArrayF32B),
263            0x52 => Ok(Self::TypedArrayF64B),
264            0x53 => Ok(Self::TypedArrayF128B),
265            0x54 => Ok(Self::TypedArrayF16L),
266            0x55 => Ok(Self::TypedArrayF32L),
267            0x56 => Ok(Self::TypedArrayF64L),
268            0x57 => Ok(Self::TypedArrayF128L),
269            0x410 => Ok(Self::MultiDimArrayC),
270            _ => Err(Unknown),
271        }
272    }
273}
274
275/// Error indicating that a tag value is not a known IANA tag.
276#[derive(#[automatically_derived]
impl ::core::fmt::Debug for Unknown {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        ::core::fmt::Formatter::write_str(f, "Unknown")
    }
}Debug, #[automatically_derived]
impl ::core::clone::Clone for Unknown {
    #[inline]
    fn clone(&self) -> Unknown { *self }
}Clone, #[automatically_derived]
impl ::core::marker::Copy for Unknown { }Copy, #[automatically_derived]
impl ::core::cmp::PartialEq for Unknown {
    #[inline]
    fn eq(&self, other: &Unknown) -> bool { true }
}PartialEq, #[automatically_derived]
impl ::core::cmp::Eq for Unknown {
    #[inline]
    #[doc(hidden)]
    #[coverage(off)]
    fn assert_receiver_is_total_eq(&self) {}
}Eq, #[automatically_derived]
impl ::core::hash::Hash for Unknown {
    #[inline]
    fn hash<__H: ::core::hash::Hasher>(&self, state: &mut __H) {}
}Hash)]
277pub struct Unknown;
278
279impl core::fmt::Display for Unknown {
280    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
281        f.write_fmt(format_args!("unknown IANA tag"))write!(f, "unknown IANA tag")
282    }
283}
284
285impl core::error::Error for Unknown {}
286
287#[cfg(test)]
288mod tests {
289    use super::*;
290    use crate::test;
291
292    #[test]
293    fn u32() {
294        let cbor = [0xc0, 0x18, 0x2a];
295        assert!(test::<Tagged<u32, 0>>(Tagged(42), &cbor).unwrap());
296    }
297
298    #[test]
299    fn string() {
300        let cbor = [
301            0xc0, 0x74, 0x32, 0x30, 0x31, 0x33, 0x2d, 0x30, 0x33, 0x2d, 0x32, 0x31, 0x54, 0x32,
302            0x30, 0x3a, 0x30, 0x34, 0x3a, 0x30, 0x30, 0x5a,
303        ];
304        assert!(test::<Tagged<&str, 0>>(Tagged("2013-03-21T20:04:00Z"), &cbor).unwrap());
305    }
306
307    #[test]
308    fn bignum() {
309        let cbor = [0xc2, 0x42, 0x01, 0x00];
310        assert!(test::<Tagged<&[u8], 2>>(Tagged(&[0x01, 0x00]), &cbor).unwrap());
311    }
312
313    #[test]
314    fn uri() {
315        let cbor = [
316            0xd8, 0x20, 0x6b, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x61, 0x2e, 0x62, 0x63,
317        ];
318        assert!(test::<Tagged<&str, 32>>(Tagged("http://a.bc"), &cbor).unwrap());
319    }
320
321    #[test]
322    fn invalid() {
323        use crate::Decoder;
324        let cbor = [0xc0, 0x18, 0x2a];
325        let err = <Tagged<u32, 1>>::decode(&mut Decoder(&cbor)).unwrap_err();
326        assert_eq!(err, Error::InvalidTag);
327    }
328
329    #[test]
330    fn untagged() {
331        use crate::Decoder;
332        let cbor = [0x18, 0x2a];
333        let err = <Tagged<u32, 0>>::decode(&mut Decoder(&cbor)).unwrap_err();
334        assert_eq!(err, InvalidHeader.into());
335    }
336}