@@ -5,6 +5,13 @@ use minicbor::decode::Error;
5
5
6
6
use crate :: data:: Type ;
7
7
8
+ /// Provides functions for decoding a CBOR object with a known schema.
9
+ ///
10
+ /// Although CBOR is a self-describing format, this decoder is tailored for cases where the schema
11
+ /// is known in advance. Therefore, the caller can determine which object key exists at the current
12
+ /// position by calling `str` method, and call the relevant function based on the predetermined schema
13
+ /// for that key. If an unexpected key is encountered, the caller can use the `skip` method to skip
14
+ /// over the element.
8
15
#[ derive( Debug , Clone ) ]
9
16
pub struct Decoder < ' b > {
10
17
decoder : minicbor:: Decoder < ' b > ,
@@ -18,15 +25,14 @@ pub struct DeserializeError {
18
25
19
26
impl std:: fmt:: Display for DeserializeError {
20
27
fn fmt ( & self , f : & mut std:: fmt:: Formatter < ' _ > ) -> std:: fmt:: Result {
21
- // TODO? Is this good enough?
22
28
self . _inner . fmt ( f)
23
29
}
24
30
}
25
31
26
32
impl std:: error:: Error for DeserializeError { }
27
33
28
34
impl DeserializeError {
29
- fn new ( inner : Error ) -> Self {
35
+ pub ( crate ) fn new ( inner : Error ) -> Self {
30
36
Self { _inner : inner }
31
37
}
32
38
@@ -55,60 +61,86 @@ impl DeserializeError {
55
61
}
56
62
}
57
63
64
+
65
+ /// Macro for delegating method calls to the decoder.
66
+ ///
67
+ /// This macro generates wrapper methods for calling specific encoder methods on the decoder
68
+ /// and returning the result with error handling.
69
+ ///
70
+ /// # Example
71
+ ///
72
+ /// ```
73
+ /// delegate_method! {
74
+ /// /// Wrapper method for encoding method `encode_str` on the decoder.
75
+ /// encode_str_wrapper => encode_str(String);
76
+ /// /// Wrapper method for encoding method `encode_int` on the decoder.
77
+ /// encode_int_wrapper => encode_int(i32);
78
+ /// }
79
+ /// ```
80
+ macro_rules! delegate_method {
81
+ ( $( $( #[ $meta: meta] ) * $wrapper_name: ident => $encoder_name: ident( $result_type: ty) ; ) +) => {
82
+ $(
83
+ pub fn $wrapper_name( & mut self ) -> Result <$result_type, DeserializeError > {
84
+ self . decoder. $encoder_name( ) . map_err( DeserializeError :: new)
85
+ }
86
+ ) +
87
+ } ;
88
+ }
89
+
58
90
impl < ' b > Decoder < ' b > {
59
91
pub fn new ( bytes : & ' b [ u8 ] ) -> Self {
60
92
Self {
61
93
decoder : minicbor:: Decoder :: new ( bytes) ,
62
94
}
63
95
}
64
96
65
- pub fn map ( & mut self ) -> Result < Option < u64 > , DeserializeError > {
66
- self . decoder . map ( ) . map_err ( DeserializeError :: new)
67
- }
68
-
69
97
pub fn datatype ( & self ) -> Result < Type , DeserializeError > {
70
98
self . decoder
71
99
. datatype ( )
72
100
. map ( Type :: new)
73
101
. map_err ( DeserializeError :: new)
74
102
}
75
103
76
- pub fn skip ( & mut self ) -> Result < ( ) , DeserializeError > {
77
- self . decoder . skip ( ) . map_err ( DeserializeError :: new )
78
- }
79
-
80
- // TODO-David: confirm benchmarks and keep either `str_alt` or `str`.
81
- // The following seems to be a bit slower than the one we have kept .
82
- pub fn str_alt ( & mut self ) -> Result < Cow < ' b , str > , DeserializeError > {
83
- // This implementation uses `next` twice to see if there is
84
- // another str chunk. If there is, it returns a owned `String`.
85
- let mut chunks_iter = self . decoder . str_iter ( ) . map_err ( DeserializeError :: new ) ? ;
86
- let head = match chunks_iter . next ( ) {
87
- Some ( Ok ( head ) ) => head ,
88
- None => return Ok ( Cow :: Borrowed ( "" ) ) ,
89
- Some ( Err ( e ) ) => return Err ( DeserializeError :: new ( e ) ) ,
90
- } ;
91
-
92
- match chunks_iter . next ( ) {
93
- None => Ok ( Cow :: Borrowed ( head ) ) ,
94
- Some ( Err ( e ) ) => Err ( DeserializeError :: new ( e ) ) ,
95
- Some ( Ok ( next ) ) => {
96
- let mut concatenated_string = String :: from ( head ) ;
97
- concatenated_string . push_str ( next ) ;
98
- for chunk in chunks_iter {
99
- concatenated_string . push_str ( chunk . map_err ( DeserializeError :: new ) ? ) ;
100
- }
101
- Ok ( Cow :: Owned ( concatenated_string ) )
102
- }
103
- }
104
+ delegate_method ! {
105
+ /// Skips the current CBOR element.
106
+ skip => skip ( ( ) ) ;
107
+ /// Reads a boolean at the current position.
108
+ boolean => bool ( bool ) ;
109
+ /// Reads a byte at the current position .
110
+ byte => i8 ( i8 ) ;
111
+ /// Reads a short at the current position.
112
+ short => i16 ( i16 ) ;
113
+ /// Reads a integer at the current position.
114
+ integer => i32 ( i32 ) ;
115
+ /// Reads a long at the current position.
116
+ long => i64 ( i64 ) ;
117
+ /// Reads a float at the current position.
118
+ float => f32 ( f32 ) ;
119
+ /// Reads a double at the current position.
120
+ double => f64 ( f64 ) ;
121
+ /// Reads a null CBOR element at the current position.
122
+ null => null ( ( ) ) ;
123
+ /// Returns the number of elements in a definite list. For indefinite lists it returns a `None`.
124
+ list => array ( Option < u64 > ) ;
125
+ /// Returns the number of elements in a definite map. For indefinite map it returns a `None`.
126
+ map => map ( Option < u64 > ) ;
127
+ }
128
+
129
+ /// Returns the current position of the buffer, which will be decoded when any of the methods is called.
130
+ pub fn position ( & self ) -> usize {
131
+ self . decoder . position ( )
104
132
}
105
133
134
+ /// Returns a `cow::Borrowed(&str)` if the element at the current position in the buffer is a definite
135
+ /// length string. Otherwise, it returns a `cow::Owned(String)` if the element at the current position is an
136
+ /// indefinite-length string. An error is returned if the element is neither a definite length nor an
137
+ /// indefinite-length string.
106
138
pub fn str ( & mut self ) -> Result < Cow < ' b , str > , DeserializeError > {
107
139
let bookmark = self . decoder . position ( ) ;
108
140
match self . decoder . str ( ) {
109
141
Ok ( str_value) => Ok ( Cow :: Borrowed ( str_value) ) ,
110
142
Err ( e) if e. is_type_mismatch ( ) => {
111
- // Move the position back to the start of the Cbor element and then try
143
+ // Move the position back to the start of the CBOR element and then try
112
144
// decoding it as a indefinite length string.
113
145
self . decoder . set_position ( bookmark) ;
114
146
Ok ( Cow :: Owned ( self . string ( ) ?) )
@@ -117,25 +149,15 @@ impl<'b> Decoder<'b> {
117
149
}
118
150
}
119
151
120
- // TODO-David: confirm benchmarks and keep either `string_alt` or `string` implementation.
121
- // The following seems to be a bit slower than the one we have kept.
122
- pub fn string_alt ( & mut self ) -> Result < String , DeserializeError > {
123
- let s: Result < String , _ > = self
124
- . decoder
125
- . str_iter ( )
126
- . map_err ( DeserializeError :: new) ?
127
- . collect ( ) ;
128
- s. map_err ( DeserializeError :: new)
129
- }
130
-
152
+ /// Allocates and returns a `String` if the element at the current position in the buffer is either a
153
+ /// definite-length or an indefinite-length string. Otherwise, an error is returned if the element is not a string type.
131
154
pub fn string ( & mut self ) -> Result < String , DeserializeError > {
132
155
let mut iter = self . decoder . str_iter ( ) . map_err ( DeserializeError :: new) ?;
133
156
let head = iter. next ( ) ;
134
157
135
158
let decoded_string = match head {
136
159
None => String :: new ( ) ,
137
160
Some ( head) => {
138
- // The following is faster in benchmarks than using `Collect()` on a `String`.
139
161
let mut combined_chunks = String :: from ( head. map_err ( DeserializeError :: new) ?) ;
140
162
for chunk in iter {
141
163
combined_chunks. push_str ( chunk. map_err ( DeserializeError :: new) ?) ;
@@ -147,6 +169,8 @@ impl<'b> Decoder<'b> {
147
169
Ok ( decoded_string)
148
170
}
149
171
172
+ /// Returns a `blob` if the element at the current position in the buffer is a byte string. Otherwise,
173
+ /// a `DeserializeError` error is returned.
150
174
pub fn blob ( & mut self ) -> Result < Blob , DeserializeError > {
151
175
let iter = self . decoder . bytes_iter ( ) . map_err ( DeserializeError :: new) ?;
152
176
let parts: Vec < & [ u8 ] > = iter
@@ -160,57 +184,20 @@ impl<'b> Decoder<'b> {
160
184
} )
161
185
}
162
186
163
- pub fn boolean ( & mut self ) -> Result < bool , DeserializeError > {
164
- self . decoder . bool ( ) . map_err ( DeserializeError :: new)
165
- }
166
-
167
- pub fn position ( & self ) -> usize {
168
- self . decoder . position ( )
169
- }
170
-
171
- pub fn byte ( & mut self ) -> Result < i8 , DeserializeError > {
172
- self . decoder . i8 ( ) . map_err ( DeserializeError :: new)
173
- }
174
-
175
- pub fn short ( & mut self ) -> Result < i16 , DeserializeError > {
176
- self . decoder . i16 ( ) . map_err ( DeserializeError :: new)
177
- }
178
-
179
- pub fn integer ( & mut self ) -> Result < i32 , DeserializeError > {
180
- self . decoder . i32 ( ) . map_err ( DeserializeError :: new)
181
- }
182
-
183
- pub fn long ( & mut self ) -> Result < i64 , DeserializeError > {
184
- self . decoder . i64 ( ) . map_err ( DeserializeError :: new)
185
- }
186
-
187
- pub fn float ( & mut self ) -> Result < f32 , DeserializeError > {
188
- self . decoder . f32 ( ) . map_err ( DeserializeError :: new)
189
- }
190
-
191
- pub fn double ( & mut self ) -> Result < f64 , DeserializeError > {
192
- self . decoder . f64 ( ) . map_err ( DeserializeError :: new)
193
- }
194
-
187
+ /// Returns a `DateTime` if the element at the current position in the buffer is a `timestamp`. Otherwise,
188
+ /// a `DeserializeError` error is returned.
195
189
pub fn timestamp ( & mut self ) -> Result < DateTime , DeserializeError > {
196
190
let tag = self . decoder . tag ( ) . map_err ( DeserializeError :: new) ?;
197
191
198
192
if !matches ! ( tag, minicbor:: data:: Tag :: Timestamp ) {
199
- // TODO
200
- todo ! ( )
193
+ Err ( DeserializeError :: new ( Error :: message (
194
+ "expected timestamp tag" ,
195
+ ) ) )
201
196
} else {
202
197
let epoch_seconds = self . decoder . f64 ( ) . map_err ( DeserializeError :: new) ?;
203
198
Ok ( DateTime :: from_secs_f64 ( epoch_seconds) )
204
199
}
205
200
}
206
-
207
- pub fn null ( & mut self ) -> Result < ( ) , DeserializeError > {
208
- self . decoder . null ( ) . map_err ( DeserializeError :: new)
209
- }
210
-
211
- pub fn list ( & mut self ) -> Result < Option < u64 > , DeserializeError > {
212
- self . decoder . array ( ) . map_err ( DeserializeError :: new)
213
- }
214
201
}
215
202
216
203
#[ derive( Debug ) ]
0 commit comments