| 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 | |