dcbor/
simple.rs

1import_stdlib!();
2
3use super::varint::{EncodeVarInt, MajorType};
4use crate::{CBOR, CBORCase, Error, Result, float::f64_cbor_data};
5
6/// Represents CBOR simple values (major type 7).
7///
8/// In CBOR, simple values are a special category that includes booleans (`true`
9/// and `false`), `null`, and floating point numbers.
10///
11/// Per Section 2.4 of the dCBOR specification, only these specific simple
12/// values are valid in dCBOR. All other major type 7 values (such as undefined
13/// or other simple values) are invalid and will be rejected by dCBOR decoders.
14///
15/// When encoding floating point values, dCBOR follows specific numeric
16/// reduction rules detailed in Section 2.3 of the dCBOR specification,
17/// including:
18/// - Integral floating point values must be reduced to integers when possible
19/// - NaN values must be normalized to the canonical form `f97e00`
20///
21/// # Note
22///
23/// This type is primarily an implementation detail. Users should generally use
24/// Rust's native types instead:
25///
26/// - Use Rust's `true` and `false` booleans directly
27/// - Use the convenience methods `CBOR::r#true()`, `CBOR::r#false()`, and
28///   `CBOR::null()`
29/// - Use Rust's floating point types like `f64` directly
30///
31/// # Examples
32///
33/// ```
34/// use dcbor::prelude::*;
35///
36/// // Use Rust's native boolean and numeric types
37/// let false_cbor = CBOR::from(false);
38/// let true_cbor = CBOR::from(true);
39/// let float_cbor = CBOR::from(3.14159);
40///
41/// // Using convenience methods for common values
42/// let false_value = CBOR::r#false();
43/// let true_value = CBOR::r#true();
44/// let null_value = CBOR::null();
45///
46/// // Verify they produce the same encodings
47/// assert_eq!(false_cbor, false_value);
48/// assert_eq!(true_cbor, true_value);
49/// ```
50#[derive(Clone)]
51pub enum Simple {
52    /// The boolean value `false`.
53    /// Encoded as `0xf4` in CBOR, or `0x14` (20) with major type 7.
54    False,
55
56    /// The boolean value `true`.
57    /// Encoded as `0xf5` in CBOR, or `0x15` (21) with major type 7.
58    True,
59
60    /// The value representing `null` (`None`).
61    /// Encoded as `0xf6` in CBOR, or `0x16` (22) with major type 7.
62    Null,
63
64    /// A floating point value.
65    ///
66    /// In dCBOR, floating point values follow these encoding rules:
67    /// - Values are encoded in the shortest form that preserves precision
68    /// - Integral floating point values are encoded as integers when in range
69    /// - NaN values are normalized to `f97e00`
70    Float(f64),
71}
72
73impl Eq for Simple {
74    fn assert_receiver_is_total_eq(&self) {}
75}
76
77impl hash::Hash for Simple {
78    fn hash<H: hash::Hasher>(&self, state: &mut H) {
79        match self {
80            Self::False => (0u8).hash(state),
81            Self::True => (1u8).hash(state),
82            Self::Null => (2u8).hash(state),
83            Self::Float(v) => v.to_bits().hash(state),
84        }
85    }
86}
87
88impl Simple {
89    /// Returns the standard name of the simple value as a string.
90    ///
91    /// For `False`, `True`, and `Null`, this returns their lowercase string
92    /// representation. For `Float` values, it returns their numeric
93    /// representation.
94    ///
95    /// # Note
96    ///
97    /// This method is primarily used internally. Users should generally
98    /// interact with Rust's native types rather than with `Simple` values
99    /// directly.
100    pub fn name(&self) -> String { format!("{:?}", self) }
101
102    /// Checks if the simple value is a floating point number.
103    pub fn is_float(&self) -> bool { matches!(self, Self::Float(_)) }
104
105    /// Checks if the simple value is the NaN (Not a Number) representation.
106    pub fn is_nan(&self) -> bool {
107        matches!(self, Self::Float(v) if v.is_nan())
108    }
109
110    /// Encodes the simple value to its raw CBOR byte representation.
111    ///
112    /// Returns the CBOR bytes that represent this simple value according to the
113    /// dCBOR deterministic encoding rules:
114    /// - `False` encodes as `0xf4`
115    /// - `True` encodes as `0xf5`
116    /// - `Null` encodes as `0xf6`
117    /// - `Float` values encode according to the IEEE 754 floating point rules,
118    ///   using the shortest representation that preserves precision.
119    ///
120    /// # Note
121    ///
122    /// This method is primarily used internally. For encoding simple values,
123    /// users should use the `to_cbor_data` method on CBOR values created from
124    /// Rust's native types.
125    pub fn cbor_data(&self) -> Vec<u8> {
126        match self {
127            Self::False => (20u8).encode_varint(MajorType::Simple),
128            Self::True => (21u8).encode_varint(MajorType::Simple),
129            Self::Null => (22u8).encode_varint(MajorType::Simple),
130            Self::Float(v) => f64_cbor_data(*v),
131        }
132    }
133}
134
135/// Converts a `Simple` value into a CBOR representation.
136///
137/// This conversion allows `Simple` values to be seamlessly used where CBOR
138/// values are expected, creating a CBOR value of type `CBORCase::Simple` that
139/// wraps the simple value.
140///
141/// # Note
142///
143/// This conversion is primarily used internally. Users should generally prefer
144/// converting from Rust's native types (bool, f64) directly to CBOR instead of
145/// using the `Simple` type.
146impl From<Simple> for CBOR {
147    fn from(value: Simple) -> Self { CBORCase::Simple(value.clone()).into() }
148}
149
150/// Attempts to convert a CBOR value to a `Simple` value.
151///
152/// If the CBOR value is a `CBORCase::Simple`, this conversion will succeed.
153/// For any other CBOR type, it will return a `WrongType` error.
154///
155/// # Note
156///
157/// This conversion is primarily used internally. Users should generally prefer
158/// converting CBOR values to Rust's native types (bool, f64, etc.) instead of
159/// to the `Simple` type.
160impl TryFrom<CBOR> for Simple {
161    type Error = Error;
162
163    fn try_from(cbor: CBOR) -> Result<Self> {
164        match cbor.into_case() {
165            CBORCase::Simple(simple) => Ok(simple),
166            _ => Err(Error::WrongType),
167        }
168    }
169}
170
171/// Implements equality comparison for `Simple` values.
172///
173/// Two `Simple` values are equal if they're the same variant. For `Float`
174/// variants, the contained floating point values are compared for equality
175/// according to Rust's floating point equality rules.
176impl PartialEq for Simple {
177    fn eq(&self, other: &Self) -> bool {
178        match (self, other) {
179            (Self::False, Self::False) => true,
180            (Self::True, Self::True) => true,
181            (Self::Null, Self::Null) => true,
182            (Self::Float(v1), Self::Float(v2)) => {
183                v1 == v2 || (v1.is_nan() && v2.is_nan())
184            }
185            _ => false,
186        }
187    }
188}
189
190/// Implements debug formatting for `Simple` values.
191///
192/// This is used to generate string representations for debugging purposes.
193/// The format matches the standard string representations of these values:
194/// - `false` for `Simple::False`
195/// - `true` for `Simple::True`
196/// - `null` for `Simple::Null`
197/// - The debug representation of the float for `Simple::Float`
198///
199/// This implementation is used internally by the `name` method.
200impl fmt::Debug for Simple {
201    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
202        let s = match self {
203            Self::False => "false".to_owned(),
204            Self::True => "true".to_owned(),
205            Self::Null => "null".to_owned(),
206            Self::Float(v) => format!("{:?}", v),
207        };
208        f.write_str(&s)
209    }
210}
211
212/// Implements string display formatting for `Simple` values.
213///
214/// This is used when converting a `Simple` value to a string, such as with
215/// `to_string()`. The format matches the standard string representations:
216/// - `false` for `Simple::False`
217/// - `true` for `Simple::True`
218/// - `null` for `Simple::Null`
219/// - The debug representation of the float for `Simple::Float`
220impl fmt::Display for Simple {
221    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
222        let s = match self {
223            Self::False => "false".to_owned(),
224            Self::True => "true".to_owned(),
225            Self::Null => "null".to_owned(),
226            Self::Float(v) => {
227                if v.is_nan() {
228                    "NaN".to_owned()
229                } else if v.is_infinite() {
230                    if v.is_sign_positive() {
231                        "Infinity".to_owned()
232                    } else {
233                        "-Infinity".to_owned()
234                    }
235                } else {
236                    format!("{:?}", v)
237                }
238            }
239        };
240        f.write_str(&s)
241    }
242}