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