Skip to main content

dcbor/
cbor_tagged.rs

1import_stdlib!();
2
3use crate::Tag;
4
5/// # CBOR Tagged Value Support
6///
7/// CBOR allows values to be "tagged" with semantic information using tag
8/// numbers. The dCBOR library provides a set of traits for working with tagged
9/// values in a type-safe manner.
10///
11/// Tags in CBOR provide additional context about how a value should be
12/// interpreted. For example, tag 1 is used for dates, indicating the value is a
13/// timestamp.
14///
15/// This trait system allows Rust types to define their associated CBOR tags
16/// and provide serialization/deserialization logic specifically for tagged
17/// values. A trait for types that have an associated CBOR tag.
18///
19/// In CBOR, tags provide semantic information about how to interpret data
20/// items. This trait defines which CBOR tag(s) are associated with a particular
21/// Rust type.
22///
23/// Implementing this trait is a prerequisite for implementing
24/// `CBORTaggedEncodable` and `CBORTaggedDecodable`.
25///
26/// ## Multiple Tags for Backward Compatibility
27///
28/// The `cbor_tags()` method returns a vector of tags, enabling support for
29/// backward compatibility with older tag versions:
30///
31/// - **When encoding**: Only the first tag in the vector is used for
32///   serialization
33/// - **When decoding**: Any of the tags in the vector will be accepted
34///
35/// This design solves several real-world problems:
36///
37/// 1. **IANA Registration Simplification**: If you initially choose a tag in
38///    the Specification Required range (24-32767) and later want to move to the
39///    simpler First Come First Served range (32768+), you can migrate while
40///    maintaining compatibility with existing data.
41///
42/// 2. **Protocol Evolution**: As your protocol evolves, you can introduce new
43///    preferred tags while still supporting data encoded with older tags.
44///
45/// 3. **Versioning**: Different tags can represent different versions of your
46///    data format while sharing the same Rust type for handling.
47///
48/// ## Example: Single Tag
49///
50/// ```
51/// use dcbor::prelude::*;
52///
53/// // Define a Date struct
54/// #[derive(Debug, Clone, PartialEq)]
55/// struct Date(f64); // Storing timestamp as seconds since epoch
56///
57/// // Associate with CBOR tag 1 (standard date tag)
58/// impl CBORTagged for Date {
59///     fn cbor_tags() -> Vec<Tag> { vec![Tag::with_value(1)] }
60/// }
61///
62/// // Now the Date type has an association with CBOR tag 1
63/// let tags = Date::cbor_tags();
64/// assert_eq!(tags[0].value(), 1);
65/// ```
66///
67/// ## Example: Multiple Tags for Backward Compatibility
68///
69/// ```
70/// use dcbor::prelude::*;
71///
72/// // Seed data structure for cryptographic operations
73/// #[derive(Debug, Clone, PartialEq)]
74/// struct Seed {
75///     bytes: [u8; 16], // Example seed data
76/// }
77///
78/// // Associate with two tags:
79/// // - Tag 40300 (First Come First Served IANA range, used for encoding)
80/// // - Tag 300 (legacy tag in the Specification Required range, still accepted for decoding)
81/// impl CBORTagged for Seed {
82///     fn cbor_tags() -> Vec<Tag> {
83///         vec![
84///             // Primary tag (used for all new encodings)
85///             Tag::with_value(40300), // Higher range (32768+) only needs First Come First Served registration
86///             
87///             // Legacy tag (accepted when decoding old data)
88///             Tag::with_value(300),   // Originally chosen tag in Specification Required range (24-32767)
89///         ]
90///     }
91/// }
92///
93/// // New data is encoded with tag 40300
94/// let seed = Seed { bytes: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15] };
95///
96/// // But when decoding, the system can still read data tagged with either
97/// // 40300 or 300, ensuring backward compatibility with existing data
98/// ```
99pub trait CBORTagged {
100    /// Returns the CBOR tags associated with this type.
101    ///
102    /// This method should return a vector of tags in order of preference:
103    ///
104    /// - The first tag in the vector is the "preferred" tag and will be used
105    ///   when encoding values of this type via
106    ///   `CBORTaggedEncodable::tagged_cbor()`.
107    ///
108    /// - All tags in the vector are considered equivalent for decoding. When
109    ///   `CBORTaggedDecodable::from_tagged_cbor()` is called, any tag in this
110    ///   vector will be accepted as valid for this type.
111    ///
112    /// This design enables backward compatibility: you can introduce a new tag
113    /// (placed first in the vector) while still supporting older tags for
114    /// decoding.
115    ///
116    /// For standard CBOR tags, you can use predefined tag constants from the
117    /// `tags` module, or create custom tags with `Tag::with_value()`.
118    fn cbor_tags() -> Vec<Tag>;
119}