dcbor/conveniences.rs
1//! # Conveniences for CBOR Values
2//!
3//! This module provides convenience functions for working with CBOR
4//! (Concise Binary Object Representation) values. It extends the `CBOR`
5//! type with a set of methods for creating, converting, and inspecting
6//! various CBOR data types.
7//!
8//! The convenience functions are organized into several categories:
9//!
10//! * **Byte Strings** - Methods for working with CBOR byte strings
11//! * **Tagged Values** - Methods for creating and extracting CBOR tagged values
12//! * **Text Strings** - Methods for working with CBOR text strings
13//! * **Arrays** - Methods for creating and manipulating CBOR arrays
14//! * **Maps** - Methods for creating and manipulating CBOR maps
15//! * **Simple Values** - Methods for working with CBOR simple values like
16//! `true`, `false`, `null`
17//! * **Numeric Values** - Methods for working with CBOR numeric types
18//!
19//! These methods make it easier to work with CBOR values in a type-safe
20//! manner, providing clearer error messages and more intuitive conversions
21//! between Rust and CBOR types.
22
23import_stdlib!();
24
25use crate::{CBOR, CBORCase, Error, Map, Result, Simple, tag::Tag};
26
27/// Conveniences for byte strings.
28impl CBOR {
29 /// Creates a new CBOR value representing a byte string.
30 ///
31 /// This method creates a CBOR byte string from any type that
32 /// can be referenced as a byte slice.
33 ///
34 /// # Arguments
35 ///
36 /// * `data` - The bytes to include in the byte string, which can be any
37 /// type that can be referenced as a byte slice (e.g., `Vec<u8>`, `&[u8]`,
38 /// etc.)
39 ///
40 /// # Returns
41 ///
42 /// A new CBOR value representing the byte string.
43 ///
44 /// # Examples
45 ///
46 /// ```no_run
47 /// use dcbor::prelude::*;
48 ///
49 /// // Create a CBOR byte string from a byte slice
50 /// let bytes = vec![0x01, 0x02, 0x03];
51 /// let cbor = CBOR::to_byte_string(&bytes);
52 ///
53 /// // Encode to CBOR binary
54 /// let encoded = cbor.to_cbor_data();
55 /// assert_eq!(hex::encode(&encoded), "43010203");
56 ///
57 /// // Convert back to bytes
58 /// let recovered: Vec<u8> = cbor.try_into().unwrap();
59 /// assert_eq!(recovered, vec![0x01, 0x02, 0x03]);
60 /// ```
61 pub fn to_byte_string(data: impl AsRef<[u8]>) -> CBOR {
62 CBORCase::ByteString(data.as_ref().into()).into()
63 }
64
65 /// Creates a new CBOR value representing a byte string from a hexadecimal
66 /// string.
67 ///
68 /// This is a convenience method that converts a hexadecimal string to a
69 /// byte array and then creates a CBOR byte string value.
70 ///
71 /// # Arguments
72 ///
73 /// * `hex` - A string containing hexadecimal characters (no spaces or other
74 /// characters)
75 ///
76 /// # Returns
77 ///
78 /// A new CBOR value representing the byte string.
79 ///
80 /// # Examples
81 ///
82 /// ```
83 /// use dcbor::prelude::*;
84 ///
85 /// // Create a CBOR byte string from a hex string
86 /// let cbor = CBOR::to_byte_string_from_hex("010203");
87 ///
88 /// // Get the diagnostic representation
89 /// assert_eq!(cbor.diagnostic(), "h'010203'");
90 /// ```
91 ///
92 /// # Panics
93 ///
94 /// This method will panic if the hex string is not well-formed hexadecimal
95 /// (contains non-hex characters or an odd number of digits).
96 pub fn to_byte_string_from_hex(hex: impl AsRef<str>) -> CBOR {
97 Self::to_byte_string(hex::decode(hex.as_ref()).unwrap())
98 }
99
100 /// Extract the CBOR value as a byte string.
101 ///
102 /// Returns `Ok` if the value is a byte string, `Err` otherwise.
103 pub fn try_into_byte_string(self) -> Result<Vec<u8>> {
104 match self.into_case() {
105 CBORCase::ByteString(b) => Ok(b.into()),
106 _ => Err(Error::WrongType),
107 }
108 }
109
110 /// Checks if a CBOR value is a byte string.
111 ///
112 /// # Arguments
113 ///
114 /// * `cbor` - A reference to a CBOR value
115 ///
116 /// # Returns
117 ///
118 /// * `true` if the value is a byte string
119 /// * `false` otherwise
120 ///
121 /// # Examples
122 ///
123 /// ```
124 /// use dcbor::prelude::*;
125 ///
126 /// let bytes = CBOR::to_byte_string(vec![1, 2, 3]);
127 /// assert!(CBOR::is_byte_string(&bytes));
128 ///
129 /// let text: CBOR = "hello".into();
130 /// assert!(!CBOR::is_byte_string(&text));
131 /// ```
132 pub fn is_byte_string(&self) -> bool {
133 matches!(self.as_case(), CBORCase::ByteString(_))
134 }
135
136 /// Attempts to convert the CBOR value into a byte string.
137 ///
138 /// # Returns
139 ///
140 /// * `Some(Vec<u8>)` if the value is a byte string
141 /// * `None` otherwise
142 ///
143 /// # Examples
144 ///
145 /// ```
146 /// use dcbor::prelude::*;
147 ///
148 /// // Create a CBOR byte string
149 /// let cbor = CBOR::to_byte_string(vec![1, 2, 3]);
150 /// assert_eq!(cbor.into_byte_string(), Some(vec![1, 2, 3]));
151 ///
152 /// // This will return None since the value is a text string, not a byte string
153 /// let text: CBOR = "hello".into();
154 /// assert_eq!(text.into_byte_string(), None);
155 /// ```
156 pub fn into_byte_string(self) -> Option<Vec<u8>> {
157 self.try_into_byte_string().ok()
158 }
159
160 /// Tries to convert a reference to a CBOR value into a byte string.
161 ///
162 /// # Arguments
163 ///
164 /// * `cbor` - A reference to a CBOR value
165 ///
166 /// # Returns
167 ///
168 /// * `Ok(Vec<u8>)` if the value is a byte string
169 /// * `Err(Error::WrongType)` otherwise
170 ///
171 /// # Examples
172 ///
173 /// ```
174 /// use dcbor::prelude::*;
175 ///
176 /// let cbor = CBOR::to_byte_string(vec![1, 2, 3]);
177 /// let bytes = CBOR::try_byte_string(&cbor).unwrap();
178 /// assert_eq!(bytes, vec![1, 2, 3]);
179 /// ```
180 pub fn try_byte_string(&self) -> Result<Vec<u8>> {
181 self.clone().try_into_byte_string()
182 }
183
184 pub fn as_byte_string(&self) -> Option<&[u8]> {
185 match self.as_case() {
186 CBORCase::ByteString(b) => Some(b),
187 _ => None,
188 }
189 }
190}
191
192/// Conveniences for tagged values.
193impl CBOR {
194 /// Creates a new CBOR value representing a tagged value.
195 ///
196 /// This method creates a CBOR tagged value by applying a tag
197 /// to another CBOR value. Tags provide semantic information about how
198 /// the tagged data should be interpreted.
199 ///
200 /// # Arguments
201 ///
202 /// * `tag` - The tag to apply, which can be any type that can be converted
203 /// to a `Tag`
204 /// * `item` - The CBOR value to tag, which can be any type that can be
205 /// converted to `CBOR`
206 ///
207 /// # Returns
208 ///
209 /// A new CBOR value representing the tagged value.
210 ///
211 /// # Examples
212 ///
213 /// ```
214 /// use dcbor::prelude::*;
215 ///
216 /// // Create a CBOR value with tag 42 applied to the string "hello"
217 /// let tagged = CBOR::to_tagged_value(42, "hello");
218 ///
219 /// // Get the diagnostic representation
220 /// assert_eq!(tagged.diagnostic(), "42(\"hello\")");
221 ///
222 /// // Extract the tag and the tagged value
223 /// let (tag, value) = tagged.try_into_tagged_value().unwrap();
224 /// assert_eq!(tag.value(), 42);
225 /// let s: String = value.try_into().unwrap();
226 /// assert_eq!(s, "hello");
227 /// ```
228 pub fn to_tagged_value(tag: impl Into<Tag>, item: impl Into<CBOR>) -> CBOR {
229 CBORCase::Tagged(tag.into(), item.into()).into()
230 }
231
232 /// Extract the CBOR value as a tagged value.
233 ///
234 /// Returns `Ok` if the value is a tagged value, `Err` otherwise.
235 pub fn try_into_tagged_value(self) -> Result<(Tag, CBOR)> {
236 match self.into_case() {
237 CBORCase::Tagged(tag, value) => Ok((tag, value)),
238 _ => Err(Error::WrongType),
239 }
240 }
241
242 pub fn is_tagged_value(&self) -> bool {
243 matches!(self.as_case(), CBORCase::Tagged(_, _))
244 }
245
246 pub fn as_tagged_value(&self) -> Option<(&Tag, &CBOR)> {
247 match self.as_case() {
248 CBORCase::Tagged(tag, value) => Some((tag, value)),
249 _ => None,
250 }
251 }
252
253 /// Tries to convert a reference to a CBOR value into a tagged value.
254 ///
255 /// # Arguments
256 ///
257 /// * `cbor` - A reference to a CBOR value
258 ///
259 /// # Returns
260 ///
261 /// * `Ok((Tag, CBOR))` - The tag and the tagged value if the CBOR value is
262 /// a tagged value
263 /// * `Err(Error::WrongType)` - If the value is not a tagged value
264 ///
265 /// # Examples
266 ///
267 /// ```
268 /// use dcbor::prelude::*;
269 ///
270 /// let tagged = CBOR::to_tagged_value(42, "hello");
271 /// let (tag, value) = CBOR::try_tagged_value(&tagged).unwrap();
272 /// assert_eq!(tag.value(), 42);
273 /// let s: String = value.try_into().unwrap();
274 /// assert_eq!(s, "hello");
275 /// ```
276 pub fn try_tagged_value(&self) -> Result<(Tag, CBOR)> {
277 self.clone().try_into_tagged_value()
278 }
279
280 /// Extract the CBOR value as an expected tagged value.
281 ///
282 /// Returns `Ok` if the value is a tagged value with the expected tag, `Err`
283 /// otherwise.
284 pub fn try_into_expected_tagged_value(
285 self,
286 expected_tag: impl Into<Tag>,
287 ) -> Result<CBOR> {
288 let (tag, value) = self.try_into_tagged_value()?;
289 let expected_tag = expected_tag.into();
290 if tag == expected_tag {
291 Ok(value)
292 } else {
293 Err(Error::WrongTag(expected_tag, tag))
294 }
295 }
296
297 /// Tries to extract a CBOR value that is tagged with an expected tag from a
298 /// reference.
299 ///
300 /// # Arguments
301 ///
302 /// * `cbor` - A reference to a CBOR value
303 /// * `expected_tag` - The tag value that is expected
304 ///
305 /// # Returns
306 ///
307 /// * `Ok(CBOR)` - The tagged value if the CBOR value is tagged with the
308 /// expected tag
309 /// * `Err(Error::WrongType)` - If the value is not a tagged value
310 /// * `Err(Error::WrongTag)` - If the value is tagged but with a different
311 /// tag
312 ///
313 /// # Examples
314 ///
315 /// ```
316 /// use dcbor::prelude::*;
317 ///
318 /// let tagged = CBOR::to_tagged_value(42, "hello");
319 ///
320 /// // This will succeed because the tag matches
321 /// let value = CBOR::try_expected_tagged_value(&tagged, 42).unwrap();
322 /// let s: String = value.try_into().unwrap();
323 /// assert_eq!(s, "hello");
324 ///
325 /// // This will fail because the tag doesn't match
326 /// let result = CBOR::try_expected_tagged_value(&tagged, 43);
327 /// assert!(result.is_err());
328 /// ```
329 pub fn try_expected_tagged_value(
330 &self,
331 expected_tag: impl Into<Tag>,
332 ) -> Result<CBOR> {
333 self.clone().try_into_expected_tagged_value(expected_tag)
334 }
335}
336
337/// Conveniences for text strings.
338impl CBOR {
339 /// Extract the CBOR value as a text string.
340 ///
341 /// Returns `Ok` if the value is a text string, `Err` otherwise.
342 pub fn try_into_text(self) -> Result<String> {
343 match self.into_case() {
344 CBORCase::Text(t) => Ok(t),
345 _ => Err(Error::WrongType),
346 }
347 }
348
349 pub fn is_text(&self) -> bool {
350 matches!(self.as_case(), CBORCase::Text(_))
351 }
352
353 pub fn try_text(&self) -> Result<String> { self.clone().try_into_text() }
354
355 pub fn into_text(self) -> Option<String> { self.try_into_text().ok() }
356
357 pub fn as_text(&self) -> Option<&str> {
358 match self.as_case() {
359 CBORCase::Text(t) => Some(t),
360 _ => None,
361 }
362 }
363}
364
365/// Conveniences for arrays.
366impl CBOR {
367 /// Extract the CBOR value as an array.
368 ///
369 /// Returns `Ok` if the value is an array, `Err` otherwise.
370 pub fn try_into_array(self) -> Result<Vec<CBOR>> {
371 match self.into_case() {
372 CBORCase::Array(a) => Ok(a),
373 _ => Err(Error::WrongType),
374 }
375 }
376
377 pub fn is_array(&self) -> bool {
378 matches!(self.as_case(), CBORCase::Array(_))
379 }
380
381 pub fn try_array(&self) -> Result<Vec<CBOR>> {
382 self.clone().try_into_array()
383 }
384
385 pub fn into_array(self) -> Option<Vec<CBOR>> { self.try_into_array().ok() }
386
387 pub fn as_array(&self) -> Option<&[CBOR]> {
388 match self.as_case() {
389 CBORCase::Array(a) => Some(a),
390 _ => None,
391 }
392 }
393}
394
395/// Conveniences for maps.
396impl CBOR {
397 /// Extract the CBOR value as a map.
398 ///
399 /// Returns `Ok` if the value is a map, `Err` otherwise.
400 pub fn try_into_map(self) -> Result<Map> {
401 match self.into_case() {
402 CBORCase::Map(m) => Ok(m),
403 _ => Err(Error::WrongType),
404 }
405 }
406
407 pub fn is_map(&self) -> bool { matches!(self.as_case(), CBORCase::Map(_)) }
408
409 pub fn try_map(&self) -> Result<Map> { self.clone().try_into_map() }
410
411 pub fn into_map(self) -> Option<Map> { self.try_into_map().ok() }
412
413 /// Extract the CBOR value as a simple value.
414 ///
415 /// Returns `Ok` if the value is a simple value, `Err` otherwise.
416 pub fn try_into_simple_value(self) -> Result<Simple> {
417 match self.into_case() {
418 CBORCase::Simple(s) => Ok(s),
419 _ => Err(Error::WrongType),
420 }
421 }
422
423 pub fn as_map(&self) -> Option<&Map> {
424 match self.as_case() {
425 CBORCase::Map(m) => Some(m),
426 _ => None,
427 }
428 }
429}
430
431/// Conveniences for booleans.
432impl CBOR {
433 /// The CBOR simple value representing `false`.
434 pub fn r#false() -> Self { CBORCase::Simple(Simple::False).into() }
435
436 /// The CBOR simple value representing `true`.
437 pub fn r#true() -> Self { CBORCase::Simple(Simple::True).into() }
438
439 pub fn as_bool(&self) -> Option<bool> {
440 match self.as_case() {
441 CBORCase::Simple(Simple::True) => Some(true),
442 CBORCase::Simple(Simple::False) => Some(false),
443 _ => None,
444 }
445 }
446 /// Extract the CBOR value as a boolean.
447 pub fn try_into_bool(self) -> Result<bool> {
448 match Self::as_bool(&self) {
449 Some(b) => Ok(b),
450 None => Err(Error::WrongType),
451 }
452 }
453
454 pub fn is_bool(&self) -> bool {
455 matches!(
456 self.as_case(),
457 CBORCase::Simple(Simple::True | Simple::False)
458 )
459 }
460
461 pub fn try_bool(&self) -> Result<bool> { self.clone().try_into_bool() }
462
463 /// Check if the CBOR value is true.
464 pub fn is_true(&self) -> bool {
465 matches!(self.as_case(), CBORCase::Simple(Simple::True))
466 }
467
468 /// Check if the CBOR value is false.
469 pub fn is_false(&self) -> bool {
470 matches!(self.as_case(), CBORCase::Simple(Simple::False))
471 }
472}
473
474/// Conveniences for simple values.
475impl CBOR {
476 /// Creates a CBOR value representing `null`.
477 ///
478 /// This is equivalent to the CBOR simple value `null`.
479 ///
480 /// # Returns
481 ///
482 /// A CBOR value representing `null`.
483 ///
484 /// # Examples
485 ///
486 /// ```
487 /// use dcbor::prelude::*;
488 ///
489 /// let null_value = CBOR::null();
490 /// assert!(null_value.is_null());
491 /// assert_eq!(null_value.diagnostic(), "null");
492 /// ```
493 pub fn null() -> Self { CBORCase::Simple(Simple::Null).into() }
494
495 /// Checks if the CBOR value is `null`.
496 ///
497 /// # Returns
498 ///
499 /// * `true` if the value is the CBOR simple value `null`
500 /// * `false` otherwise
501 ///
502 /// # Examples
503 ///
504 /// ```
505 /// use dcbor::prelude::*;
506 ///
507 /// let null_value = CBOR::null();
508 /// assert!(null_value.is_null());
509 ///
510 /// let other_value: CBOR = 42u64.into();
511 /// assert!(!other_value.is_null());
512 /// ```
513 pub fn is_null(&self) -> bool {
514 matches!(self.as_case(), CBORCase::Simple(Simple::Null))
515 }
516}
517
518/// Conveniences for numeric values.
519impl CBOR {
520 /// Checks if the CBOR value represents a number.
521 ///
522 /// A CBOR value is considered to be a number if it is:
523 /// - An unsigned integer (major type 0)
524 /// - A negative integer (major type 1)
525 /// - A floating-point value (major type 7, simple values 25, 26, or 27)
526 ///
527 /// # Returns
528 ///
529 /// * `true` if the value represents a number
530 /// * `false` otherwise
531 ///
532 /// # Examples
533 ///
534 /// ```
535 /// use dcbor::{CBORCase, Simple, prelude::*};
536 ///
537 /// let unsigned: CBOR = 42u64.into();
538 /// assert!(unsigned.is_number());
539 ///
540 /// let negative: CBOR = (-42i64).into();
541 /// assert!(negative.is_number());
542 ///
543 /// let float: CBOR = CBORCase::Simple(Simple::Float(3.14)).into();
544 /// assert!(float.is_number());
545 ///
546 /// let text: CBOR = "not a number".into();
547 /// assert!(!text.is_number());
548 /// ```
549 pub fn is_number(&self) -> bool {
550 match self.as_case() {
551 CBORCase::Unsigned(_) | CBORCase::Negative(_) => true,
552 CBORCase::Simple(s) => s.is_float(),
553 _ => false,
554 }
555 }
556
557 /// Checks if the CBOR value represents the NaN (Not a Number) value.
558 ///
559 /// # Returns
560 ///
561 /// * `true` if the value is the CBOR representation of NaN
562 /// * `false` otherwise
563 ///
564 /// # Examples
565 ///
566 /// ```
567 /// use dcbor::{CBORCase, Simple, prelude::*};
568 ///
569 /// let nan_value = CBOR::nan();
570 /// assert!(nan_value.is_nan());
571 ///
572 /// let float: CBOR = CBORCase::Simple(Simple::Float(3.14)).into();
573 /// assert!(!float.is_nan());
574 /// ```
575 pub fn is_nan(&self) -> bool {
576 match self.as_case() {
577 CBORCase::Simple(s) => s.is_nan(),
578 _ => false,
579 }
580 }
581
582 /// Creates a CBOR value representing NaN (Not a Number).
583 ///
584 /// # Returns
585 ///
586 /// A CBOR value representing NaN.
587 ///
588 /// # Examples
589 ///
590 /// ```
591 /// use dcbor::prelude::*;
592 ///
593 /// let nan_value = CBOR::nan();
594 /// assert!(nan_value.is_nan());
595 /// ```
596 pub fn nan() -> Self { CBORCase::Simple(Simple::Float(f64::NAN)).into() }
597}