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