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 | |
104 | #[doc (inline)] |
105 | pub use crate::ser::into_writer; |
106 | |
107 | #[doc (inline)] |
108 | pub use crate::value::Value; |
109 | |
110 | /// Build a `Value` conveniently. |
111 | /// |
112 | /// The syntax should be intuitive if you are familiar with JSON. You can also |
113 | /// inline simple Rust expressions, including custom values that implement |
114 | /// `serde::Serialize`. Note that this macro returns `Result<Value, Error>`, |
115 | /// so you should handle the error appropriately. |
116 | /// |
117 | /// ``` |
118 | /// use ciborium::cbor; |
119 | /// |
120 | /// let value = cbor!({ |
121 | /// "code" => 415, |
122 | /// "message" => null, |
123 | /// "continue" => false, |
124 | /// "extra" => { "numbers" => [8.2341e+4, 0.251425] }, |
125 | /// }).unwrap(); |
126 | /// ``` |
127 | #[macro_export ] |
128 | macro_rules! cbor { |
129 | (@map {$($key:expr => $val:expr),*} $(,)?) => {{ |
130 | $crate::value::Value::Map(vec![ |
131 | $( |
132 | (cbor!( $key )?, cbor!( $val )?) |
133 | ),* |
134 | ]) |
135 | }}; |
136 | |
137 | (@map {$($key:expr => $val:expr),*} { $($nkey:tt)* } => $($next:tt)*) => { |
138 | cbor!( |
139 | @map |
140 | { $($key => $val),* } |
141 | cbor!({ $($nkey)* })? => |
142 | $($next)* |
143 | ) |
144 | }; |
145 | |
146 | (@map {$($key:expr => $val:expr),*} [ $($nkey:tt)* ] => $($next:tt)*) => { |
147 | cbor!( |
148 | @map |
149 | { $($key => $val),* } |
150 | cbor!([ $($nkey)* ])? => |
151 | $($next)* |
152 | ) |
153 | }; |
154 | |
155 | (@map {$($key:expr => $val:expr),*} $nkey:expr => { $($nval:tt)* }, $($next:tt)*) => { |
156 | cbor!( |
157 | @map |
158 | { $($key => $val,)* $nkey => cbor!({ $($nval)* })? } |
159 | $($next)* |
160 | ) |
161 | }; |
162 | |
163 | (@map {$($key:expr => $val:expr),*} $nkey:expr => [ $($nval:tt)* ], $($next:tt)*) => { |
164 | cbor!( |
165 | @map |
166 | { $($key => $val,)* $nkey => cbor!([ $($nval)* ])? } |
167 | $($next)* |
168 | ) |
169 | }; |
170 | |
171 | (@map {$($key:expr => $val:expr),*} $nkey:expr => $nval:expr, $($next:tt)*) => { |
172 | cbor!( |
173 | @map |
174 | { $($key => $val,)* $nkey => cbor!($nval)? } |
175 | $($next)* |
176 | ) |
177 | }; |
178 | |
179 | (@seq [$($val:expr),*] $(,)?) => { |
180 | $crate::value::Value::Array( |
181 | vec![$( cbor!($val)? ),*] |
182 | ) |
183 | }; |
184 | |
185 | (@seq [$($val:expr),*] { $($item:tt)* }, $($next:tt)*) => { |
186 | cbor!( |
187 | @seq |
188 | [ $($val,)* cbor!({ $($item)* })? ] |
189 | $($next)* |
190 | ) |
191 | }; |
192 | |
193 | (@seq [$($val:expr),*] [ $($item:tt)* ], $($next:tt)*) => { |
194 | cbor!( |
195 | @seq |
196 | [ $($val,)* cbor!([ $($item)* ])? ] |
197 | $($next)* |
198 | ) |
199 | }; |
200 | |
201 | (@seq [$($val:expr),*] $item:expr, $($next:tt)*) => { |
202 | cbor!( |
203 | @seq |
204 | [ $($val,)* $item ] |
205 | $($next)* |
206 | ) |
207 | }; |
208 | |
209 | ({ $($next:tt)* }) => {(||{ |
210 | ::core::result::Result::<_, $crate::value::Error>::from(Ok(cbor!(@map {} $($next)* ,))) |
211 | })()}; |
212 | |
213 | ([ $($next:tt)* ]) => {(||{ |
214 | ::core::result::Result::<_, $crate::value::Error>::from(Ok(cbor!(@seq [] $($next)* ,))) |
215 | })()}; |
216 | |
217 | ($val:expr) => {{ |
218 | #[allow(unused_imports)] |
219 | use $crate::value::Value::Null as null; |
220 | $crate::value::Value::serialized(&$val) |
221 | }}; |
222 | } |
223 | |