1 // SPDX-License-Identifier: Apache-2.0
2 
3 //! Welcome to Ciborium!
4 //!
5 //! Ciborium contains CBOR serialization and deserialization implementations for serde.
6 //!
7 //! # Quick Start
8 //!
9 //! You're probably looking for [`from_reader()`](crate::de::from_reader)
10 //! and [`into_writer()`](crate::ser::into_writer), which are
11 //! the main functions. Note that byte slices are also readers and writers and can be
12 //! passed to these functions just as streams can.
13 //!
14 //! For dynamic CBOR value creation/inspection, see [`Value`](crate::value::Value).
15 //!
16 //! # Design Decisions
17 //!
18 //! ## Always Serialize Numeric Values to the Smallest Size
19 //!
20 //! Although the CBOR specification has differing numeric widths, this is only
21 //! a form of compression on the wire and is not intended to directly
22 //! represent an "integer width" or "float width." Therefore, ciborium always
23 //! serializes numbers to the smallest possible lossless encoding. For example,
24 //! we serialize `1u128` as a single byte (`01`). Likewise, we will also freely
25 //! decode that single byte into a `u128`.
26 //!
27 //! While there is some minor performance cost for this, there are several
28 //! reasons for this choice. First, the specification seems to imply it by
29 //! using a separate bit for the sign. Second, the specification requires
30 //! that implementations handle leading zeroes; a liberal reading of which
31 //! implies a requirement for lossless coercion. Third, dynamic languages like
32 //! Python have no notion of "integer width," making this is a practical
33 //! choice for maximizing wire compatibility with those languages.
34 //!
35 //! This coercion is **always** lossless. For floats, this implies that we
36 //! only coerce to a smaller size if coercion back to the original size has
37 //! the same raw bits as the original.
38 //!
39 //! ## Compatibility with Other Implementations
40 //!
41 //! The ciborium project follows the [Robustness Principle](https://en.wikipedia.org/wiki/Robustness_principle).
42 //! Therefore, we aim to be liberal in what we accept. This implies that we
43 //! aim to be wire-compatible with other implementations in decoding, but
44 //! not necessarily encoding.
45 //!
46 //! One notable example of this is that `serde_cbor` uses fixed-width encoding
47 //! of numbers and doesn't losslessly coerce. This implies that `ciborium` will
48 //! successfully decode `serde_cbor` encodings, but the opposite may not be the
49 //! case.
50 //!
51 //! ## Representing Map as a Sequence of Values
52 //!
53 //! Other serde parsers have generally taken the route of using `BTreeMap` or
54 //! `HashMap` to implement their encoding's underlying `Map` type. This crate
55 //! chooses to represent the `Map` type using `Vec<(Value, Value)>` instead.
56 //!
57 //! This decision was made because this type preserves the order of the pairs
58 //! on the wire. Further, for those that need the properties of `BTreeMap` or
59 //! `HashMap`, you can simply `collect()` the values into the respective type.
60 //! This provides maximum flexibility.
61 //!
62 //! ## Low-level Library
63 //!
64 //! The ciborium crate has the beginnings of a low-level library in the
65 //! (private) `basic` module. We may extend this to be more robust and expose
66 //! it for application consumption once we have it in a good state. If you'd
67 //! like to collaborate with us on that, please contact us. Alternatively,
68 //! we might fork this code into a separate crate with no serde dependency.
69 //!
70 //! ## Internal Types
71 //!
72 //! The ciborium crate contains a number of internal types that implement
73 //! useful serde traits. While these are not currently exposed, we might
74 //! choose to expose them in the future if there is demand. Generally, this
75 //! crate takes a conservative approach to exposing APIs to avoid breakage.
76 //!
77 //! ## Packed Encoding?
78 //!
79 //! Packed encoding uses numerical offsets to represent structure field names
80 //! and enum variant names. This can save significant space on the wire.
81 //!
82 //! While the authors of this crate like packed encoding, it should generally
83 //! be avoided because it can be fragile as it exposes invariants of your Rust
84 //! code to remote actors. We might consider adding this in the future. If you
85 //! are interested in this, please contact us.
86 
87 #![cfg_attr(not(feature = "std"), no_std)]
88 #![deny(missing_docs)]
89 #![deny(clippy::all)]
90 #![deny(clippy::cargo)]
91 #![allow(clippy::unit_arg)]
92 
93 extern crate alloc;
94 
95 pub mod de;
96 pub mod ser;
97 pub mod tag;
98 pub mod value;
99 
100 // Re-export the [items recommended by serde](https://serde.rs/conventions.html).
101 #[doc(inline)]
102 pub use crate::de::from_reader;
103 #[doc(inline)]
104 pub use crate::de::from_reader_with_buffer;
105 
106 #[doc(inline)]
107 pub use crate::ser::into_writer;
108 
109 #[doc(inline)]
110 pub use crate::value::Value;
111 
112 /// Build a `Value` conveniently.
113 ///
114 /// The syntax should be intuitive if you are familiar with JSON. You can also
115 /// inline simple Rust expressions, including custom values that implement
116 /// `serde::Serialize`. Note that this macro returns `Result<Value, Error>`,
117 /// so you should handle the error appropriately.
118 ///
119 /// ```
120 /// use ciborium::cbor;
121 ///
122 /// let value = cbor!({
123 ///     "code" => 415,
124 ///     "message" => null,
125 ///     "continue" => false,
126 ///     "extra" => { "numbers" => [8.2341e+4, 0.251425] },
127 /// }).unwrap();
128 /// ```
129 #[macro_export]
130 macro_rules! cbor {
131     (@map {$($key:expr => $val:expr),*} $(,)?) => {{
132         $crate::value::Value::Map(vec![
133             $(
134                 (cbor!( $key )?, cbor!( $val )?)
135             ),*
136         ])
137     }};
138 
139     (@map {$($key:expr => $val:expr),*} { $($nkey:tt)* } => $($next:tt)*) => {
140         cbor!(
141             @map
142             { $($key => $val),* }
143             cbor!({ $($nkey)* })? =>
144             $($next)*
145         )
146     };
147 
148     (@map {$($key:expr => $val:expr),*} [ $($nkey:tt)* ] => $($next:tt)*) => {
149         cbor!(
150             @map
151             { $($key => $val),* }
152             cbor!([ $($nkey)* ])? =>
153             $($next)*
154         )
155     };
156 
157     (@map {$($key:expr => $val:expr),*} $nkey:expr => { $($nval:tt)* }, $($next:tt)*) => {
158         cbor!(
159             @map
160             { $($key => $val,)* $nkey => cbor!({ $($nval)* })? }
161             $($next)*
162         )
163     };
164 
165     (@map {$($key:expr => $val:expr),*} $nkey:expr => [ $($nval:tt)* ], $($next:tt)*) => {
166         cbor!(
167             @map
168             { $($key => $val,)* $nkey => cbor!([ $($nval)* ])? }
169             $($next)*
170         )
171     };
172 
173     (@map {$($key:expr => $val:expr),*} $nkey:expr => $nval:expr, $($next:tt)*) => {
174         cbor!(
175             @map
176             { $($key => $val,)* $nkey => cbor!($nval)? }
177             $($next)*
178         )
179     };
180 
181     (@seq [$($val:expr),*] $(,)?) => {
182         $crate::value::Value::Array(
183             vec![$( cbor!($val)? ),*]
184         )
185     };
186 
187     (@seq [$($val:expr),*] { $($item:tt)* }, $($next:tt)*) => {
188         cbor!(
189             @seq
190             [ $($val,)* cbor!({ $($item)* })? ]
191             $($next)*
192         )
193     };
194 
195     (@seq [$($val:expr),*] [ $($item:tt)* ], $($next:tt)*) => {
196         cbor!(
197             @seq
198             [ $($val,)* cbor!([ $($item)* ])? ]
199             $($next)*
200         )
201     };
202 
203     (@seq [$($val:expr),*] $item:expr, $($next:tt)*) => {
204         cbor!(
205             @seq
206             [ $($val,)* $item ]
207             $($next)*
208         )
209     };
210 
211     ({ $($next:tt)* }) => {(||{
212         ::core::result::Result::<_, $crate::value::Error>::from(Ok(cbor!(@map {} $($next)* ,)))
213     })()};
214 
215     ([ $($next:tt)* ]) => {(||{
216         ::core::result::Result::<_, $crate::value::Error>::from(Ok(cbor!(@seq [] $($next)* ,)))
217     })()};
218 
219     ($val:expr) => {{
220         #[allow(unused_imports)]
221         use $crate::value::Value::Null as null;
222         $crate::value::Value::serialized(&$val)
223     }};
224 }
225