1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
// SPDX-License-Identifier: Apache-2.0

//! Welcome to Ciborium!
//!
//! Ciborium contains CBOR serialization and deserialization implementations for serde.
//!
//! # Quick Start
//!
//! You're probably looking for [`de::from_reader()`](crate::de::from_reader)
//! and [`ser::into_writer()`](crate::ser::into_writer), which are
//! the main functions. Note that byte slices are also readers and writers and can be
//! passed to these functions just as streams can.
//!
//! For dynamic CBOR value creation/inspection, see [`value::Value`](crate::value::Value).
//!
//! # Design Decisions
//!
//! ## Always Serialize Numeric Values to the Smallest Size
//!
//! Although the CBOR specification has differing numeric widths, this is only
//! a form of compression on the wire and is not intended to directly
//! represent an "integer width" or "float width." Therefore, ciborium always
//! serializes numbers to the smallest possible lossless encoding. For example,
//! we serialize `1u128` as a single byte (`01`). Likewise, we will also freely
//! decode that single byte into a `u128`.
//!
//! While there is some minor performance cost for this, there are several
//! reasons for this choice. First, the specification seems to imply it by
//! using a separate bit for the sign. Second, the specification requires
//! that implementations handle leading zeroes; a liberal reading of which
//! implies a requirement for lossless coercion. Third, dynamic languages like
//! Python have no notion of "integer width," making this is a practical
//! choice for maximizing wire compatibility with those languages.
//!
//! This coercion is **always** lossless. For floats, this implies that we
//! only coerce to a smaller size if coercion back to the original size has
//! the same raw bits as the original.
//!
//! ## Compatibility with Other Implementations
//!
//! The ciborium project follows the [Robustness Principle](https://en.wikipedia.org/wiki/Robustness_principle).
//! Therefore, we aim to be liberal in what we accept. This implies that we
//! aim to be wire-compatible with other implementations in decoding, but
//! not necessarily encoding.
//!
//! One notable example of this is that `serde_cbor` uses fixed-width encoding
//! of numbers and doesn't losslessly coerce. This implies that `ciborium` will
//! successfully decode `serde_cbor` encodings, but the opposite may not be the
//! case.
//!
//! ## Representing Map as a Sequence of Values
//!
//! Other serde parsers have generally taken the route of using `BTreeMap` or
//! `HashMap` to implement their encoding's underlying `Map` type. This crate
//! chooses to represent the `Map` type using `Vec<(Value, Value)>` instead.
//!
//! This decision was made because this type preserves the order of the pairs
//! on the wire. Further, for those that need the properties of `BTreeMap` or
//! `HashMap`, you can simply `collect()` the values into the respective type.
//! This provides maximum flexibility.
//!
//! ## Low-level Library
//!
//! The ciborium crate has the beginnings of a low-level library in the
//! (private) `basic` module. We may extend this to be more robust and expose
//! it for application consumption once we have it in a good state. If you'd
//! like to collaborate with us on that, please contact us. Alternatively,
//! we might fork this code into a separate crate with no serde dependency.
//!
//! ## Internal Types
//!
//! The ciborium crate contains a number of internal types that implement
//! useful serde traits. While these are not currently exposed, we might
//! choose to expose them in the future if there is demand. Generally, this
//! crate takes a conservative approach to exposing APIs to avoid breakage.
//!
//! ## Packed Encoding?
//!
//! Packed encoding uses numerical offsets to represent structure field names
//! and enum variant names. This can save significant space on the wire.
//!
//! While the authors of this crate like packed encoding, it should generally
//! be avoided because it can be fragile as it exposes invariants of your Rust
//! code to remote actors. We might consider adding this in the future. If you
//! are interested in this, please contact us.

#![cfg_attr(not(feature = "std"), no_std)]
#![deny(missing_docs)]
#![deny(clippy::all)]
#![deny(clippy::cargo)]
#![allow(clippy::unit_arg)]

extern crate alloc;

pub mod de;
pub mod ser;
pub mod tag;
pub mod value;

/// Build a `Value` conveniently.
///
/// The syntax should be intuitive if you are familiar with JSON. You can also
/// inline simple Rust expressions, including custom values that implement
/// `serde::Serialize`. Note that this macro returns `Result<Value, Error>`,
/// so you should handle the error appropriately.
///
/// ```
/// use ciborium::cbor;
///
/// let value = cbor!({
///     "code" => 415,
///     "message" => null,
///     "continue" => false,
///     "extra" => { "numbers" => [8.2341e+4, 0.251425] },
/// }).unwrap();
/// ```
#[macro_export]
macro_rules! cbor {
    (@map {$($key:expr => $val:expr),*} $(,)?) => {{
        $crate::value::Value::Map(vec![
            $(
                (cbor!( $key )?, cbor!( $val )?)
            ),*
        ])
    }};

    (@map {$($key:expr => $val:expr),*} { $($nkey:tt)* } => $($next:tt)*) => {
        cbor!(
            @map
            { $($key => $val),* }
            cbor!({ $($nkey)* })? =>
            $($next)*
        )
    };

    (@map {$($key:expr => $val:expr),*} [ $($nkey:tt)* ] => $($next:tt)*) => {
        cbor!(
            @map
            { $($key => $val),* }
            cbor!([ $($nkey)* ])? =>
            $($next)*
        )
    };

    (@map {$($key:expr => $val:expr),*} $nkey:expr => { $($nval:tt)* }, $($next:tt)*) => {
        cbor!(
            @map
            { $($key => $val,)* $nkey => cbor!({ $($nval)* })? }
            $($next)*
        )
    };

    (@map {$($key:expr => $val:expr),*} $nkey:expr => [ $($nval:tt)* ], $($next:tt)*) => {
        cbor!(
            @map
            { $($key => $val,)* $nkey => cbor!([ $($nval)* ])? }
            $($next)*
        )
    };

    (@map {$($key:expr => $val:expr),*} $nkey:expr => $nval:expr, $($next:tt)*) => {
        cbor!(
            @map
            { $($key => $val,)* $nkey => cbor!($nval)? }
            $($next)*
        )
    };

    (@seq [$($val:expr),*] $(,)?) => {
        $crate::value::Value::Array(
            vec![$( cbor!($val)? ),*]
        )
    };

    (@seq [$($val:expr),*] { $($item:tt)* }, $($next:tt)*) => {
        cbor!(
            @seq
            [ $($val,)* cbor!({ $($item)* })? ]
            $($next)*
        )
    };

    (@seq [$($val:expr),*] [ $($item:tt)* ], $($next:tt)*) => {
        cbor!(
            @seq
            [ $($val,)* cbor!([ $($item)* ])? ]
            $($next)*
        )
    };

    (@seq [$($val:expr),*] $item:expr, $($next:tt)*) => {
        cbor!(
            @seq
            [ $($val,)* $item ]
            $($next)*
        )
    };

    ({ $($next:tt)* }) => {(||{
        ::core::result::Result::<_, $crate::value::Error>::from(Ok(cbor!(@map {} $($next)* ,)))
    })()};

    ([ $($next:tt)* ]) => {(||{
        ::core::result::Result::<_, $crate::value::Error>::from(Ok(cbor!(@seq [] $($next)* ,)))
    })()};

    ($val:expr) => {{
        #[allow(unused_imports)]
        use $crate::value::Value::Null as null;
        $crate::value::Value::serialized(&$val)
    }};
}