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
93extern crate alloc;
94
95pub mod de;
96pub mod ser;
97pub mod tag;
98pub mod value;
99
100// Re-export the [items recommended by serde](https://serde.rs/conventions.html).
101#[doc(inline)]
102pub use crate::de::from_reader;
103
104#[doc(inline)]
105pub use crate::ser::into_writer;
106
107#[doc(inline)]
108pub 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]
128macro_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