1
1
//! Document Payload Content Type.
2
2
3
- use std:: {
4
- fmt:: { Display , Formatter } ,
5
- str:: FromStr ,
6
- } ;
3
+ use std:: { str:: FromStr , string:: ToString } ;
7
4
8
5
use strum:: VariantArray ;
9
6
10
7
/// Payload Content Type.
11
- #[ derive( Debug , Copy , Clone , PartialEq , Eq , VariantArray ) ]
8
+ #[ derive( Debug , Copy , Clone , PartialEq , Eq , VariantArray , strum_macros :: Display ) ]
12
9
pub enum ContentType {
13
10
/// `application/cbor`
11
+ #[ strum( to_string = "application/cbor" ) ]
14
12
Cbor ,
15
13
/// `application/cddl`
14
+ #[ strum( to_string = "application/cddl" ) ]
16
15
Cddl ,
17
16
/// `application/json`
17
+ #[ strum( to_string = "application/json" ) ]
18
18
Json ,
19
19
/// `application/json+schema`
20
+ #[ strum( to_string = "application/json+schema" ) ]
20
21
JsonSchema ,
21
- }
22
-
23
- impl Display for ContentType {
24
- fn fmt (
25
- & self ,
26
- f : & mut Formatter < ' _ > ,
27
- ) -> Result < ( ) , std:: fmt:: Error > {
28
- match self {
29
- Self :: Cbor => write ! ( f, "application/cbor" ) ,
30
- Self :: Cddl => write ! ( f, "application/cddl" ) ,
31
- Self :: Json => write ! ( f, "application/json" ) ,
32
- Self :: JsonSchema => write ! ( f, "application/json+schema" ) ,
33
- }
34
- }
22
+ /// `text/css; charset=utf-8`
23
+ #[ strum( to_string = "text/css; charset=utf-8" ) ]
24
+ Css ,
25
+ /// `text/css; charset=utf-8; template=handlebars`
26
+ #[ strum( to_string = "text/css; charset=utf-8; template=handlebars" ) ]
27
+ CssHandlebars ,
28
+ /// `text/html; charset=utf-8`
29
+ #[ strum( to_string = "text/html; charset=utf-8" ) ]
30
+ Html ,
31
+ /// `text/html; charset=utf-8; template=handlebars`
32
+ #[ strum( to_string = "text/html; charset=utf-8; template=handlebars" ) ]
33
+ HtmlHandlebars ,
34
+ /// `text/markdown; charset=utf-8`
35
+ #[ strum( to_string = "text/markdown; charset=utf-8" ) ]
36
+ Markdown ,
37
+ /// `text/markdown; charset=utf-8; template=handlebars`
38
+ #[ strum( to_string = "text/markdown; charset=utf-8; template=handlebars" ) ]
39
+ MarkdownHandlebars ,
40
+ /// `text/plain; charset=utf-8`
41
+ #[ strum( to_string = "text/plain; charset=utf-8" ) ]
42
+ Plain ,
43
+ /// `text/plain; charset=utf-8; template=handlebars`
44
+ #[ strum( to_string = "text/plain; charset=utf-8; template=handlebars" ) ]
45
+ PlainHandlebars ,
35
46
}
36
47
37
48
impl FromStr for ContentType {
@@ -43,6 +54,14 @@ impl FromStr for ContentType {
43
54
"application/cddl" => Ok ( Self :: Cddl ) ,
44
55
"application/json" => Ok ( Self :: Json ) ,
45
56
"application/json+schema" => Ok ( Self :: JsonSchema ) ,
57
+ "text/css; charset=utf-8" => Ok ( Self :: Css ) ,
58
+ "text/css; charset=utf-8; template=handlebars" => Ok ( Self :: CssHandlebars ) ,
59
+ "text/html; charset=utf-8" => Ok ( Self :: Html ) ,
60
+ "text/html; charset=utf-8; template=handlebars" => Ok ( Self :: HtmlHandlebars ) ,
61
+ "text/markdown; charset=utf-8" => Ok ( Self :: Markdown ) ,
62
+ "text/markdown; charset=utf-8; template=handlebars" => Ok ( Self :: MarkdownHandlebars ) ,
63
+ "text/plain; charset=utf-8" => Ok ( Self :: Plain ) ,
64
+ "text/plain; charset=utf-8; template=handlebars" => Ok ( Self :: PlainHandlebars ) ,
46
65
_ => {
47
66
anyhow:: bail!(
48
67
"Unsupported Content Type: {s:?}, Supported only: {:?}" ,
@@ -56,6 +75,25 @@ impl FromStr for ContentType {
56
75
}
57
76
}
58
77
78
+ impl TryFrom < u64 > for ContentType {
79
+ type Error = anyhow:: Error ;
80
+
81
+ fn try_from ( value : u64 ) -> Result < Self , Self :: Error > {
82
+ // https://www.iana.org/assignments/core-parameters/core-parameters.xhtml#content-formats
83
+ match value {
84
+ 0 => Ok ( Self :: Plain ) ,
85
+ 50 => Ok ( Self :: Json ) ,
86
+ 60 => Ok ( Self :: Cbor ) ,
87
+ 20000 => Ok ( Self :: Css ) ,
88
+ _ => {
89
+ anyhow:: bail!(
90
+ "Unsupported CoAP Content-Format: {value}, Supported only: 0, 50, 60, 20000" ,
91
+ )
92
+ } ,
93
+ }
94
+ }
95
+ }
96
+
59
97
impl < ' de > serde:: Deserialize < ' de > for ContentType {
60
98
fn deserialize < D > ( deserializer : D ) -> Result < Self , D :: Error >
61
99
where D : serde:: Deserializer < ' de > {
@@ -94,61 +132,114 @@ impl minicbor::Decode<'_, ()> for ContentType {
94
132
_ctx : & mut ( ) ,
95
133
) -> Result < Self , minicbor:: decode:: Error > {
96
134
let p = d. position ( ) ;
97
- match d. int ( ) {
98
- // CoAP Content Format JSON
99
- Ok ( val) if val == minicbor:: data:: Int :: from ( 50_u8 ) => Ok ( Self :: Json ) ,
100
- // CoAP Content Format CBOR
101
- Ok ( val) if val == minicbor:: data:: Int :: from ( 60_u8 ) => Ok ( Self :: Cbor ) ,
102
- Ok ( val) => {
103
- Err ( minicbor:: decode:: Error :: message ( format ! (
104
- "unsupported CoAP Content Formats value: {val}"
105
- ) ) )
106
- } ,
107
- Err ( _) => {
108
- d. set_position ( p) ;
109
- d. str ( ) ?. parse ( ) . map_err ( minicbor:: decode:: Error :: message)
110
- } ,
135
+ if let Ok ( val) = d. int ( ) {
136
+ let val: u64 = val. try_into ( ) . map_err ( minicbor:: decode:: Error :: custom) ?;
137
+ Self :: try_from ( val) . map_err ( minicbor:: decode:: Error :: message)
138
+ } else {
139
+ d. set_position ( p) ;
140
+ d. str ( ) ?. parse ( ) . map_err ( minicbor:: decode:: Error :: message)
111
141
}
112
142
}
113
143
}
114
144
115
145
#[ cfg( test) ]
116
146
mod tests {
147
+ use minicbor:: { Decode , Decoder , Encoder } ;
148
+ use test_case:: test_case;
149
+
117
150
use super :: * ;
118
151
119
- #[ test]
120
- fn content_type_string_test ( ) {
121
- assert_eq ! (
122
- ContentType :: from_str( "application/cbor" ) . unwrap( ) ,
123
- ContentType :: Cbor
124
- ) ;
125
- assert_eq ! (
126
- ContentType :: from_str( "application/cddl" ) . unwrap( ) ,
127
- ContentType :: Cddl
128
- ) ;
129
- assert_eq ! (
130
- ContentType :: from_str( "application/json" ) . unwrap( ) ,
131
- ContentType :: Json
132
- ) ;
133
- assert_eq ! (
134
- ContentType :: from_str( "application/json+schema" ) . unwrap( ) ,
135
- ContentType :: JsonSchema
136
- ) ;
137
- assert_eq ! (
138
- "application/cbor" . parse:: <ContentType >( ) . unwrap( ) ,
139
- ContentType :: Cbor
140
- ) ;
141
- assert_eq ! (
142
- "application/cddl" . parse:: <ContentType >( ) . unwrap( ) ,
143
- ContentType :: Cddl
144
- ) ;
145
- assert_eq ! (
146
- "application/json" . parse:: <ContentType >( ) . unwrap( ) ,
147
- ContentType :: Json
148
- ) ;
149
- assert_eq ! (
150
- "application/json+schema" . parse:: <ContentType >( ) . unwrap( ) ,
151
- ContentType :: JsonSchema
152
- ) ;
152
+ #[ test_case(
153
+ ( "application/cbor" , ContentType :: Cbor ) ;
154
+ "application/cbor"
155
+ ) ]
156
+ #[ test_case(
157
+ ( "application/cddl" , ContentType :: Cddl ) ;
158
+ "application/cddl"
159
+ ) ]
160
+ #[ test_case(
161
+ ( "application/json" , ContentType :: Json ) ;
162
+ "application/json"
163
+ ) ]
164
+ #[ test_case(
165
+ ( "application/json+schema" , ContentType :: JsonSchema ) ;
166
+ "application/json+schema"
167
+ ) ]
168
+ #[ test_case(
169
+ ( "text/css; charset=utf-8" , ContentType :: Css ) ;
170
+ "text/css; charset=utf-8"
171
+ ) ]
172
+ #[ test_case(
173
+ ( "text/css; charset=utf-8; template=handlebars" , ContentType :: CssHandlebars ) ;
174
+ "text/css; charset=utf-8; template=handlebars"
175
+ ) ]
176
+ #[ test_case(
177
+ ( "text/html; charset=utf-8" , ContentType :: Html ) ;
178
+ "text/html; charset=utf-8"
179
+ ) ]
180
+ #[ test_case(
181
+ ( "text/html; charset=utf-8; template=handlebars" , ContentType :: HtmlHandlebars ) ;
182
+ "text/html; charset=utf-8; template=handlebars"
183
+ ) ]
184
+ #[ test_case(
185
+ ( "text/markdown; charset=utf-8" , ContentType :: Markdown ) ;
186
+ "text/markdown; charset=utf-8"
187
+ ) ]
188
+ #[ test_case(
189
+ ( "text/markdown; charset=utf-8; template=handlebars" , ContentType :: MarkdownHandlebars ) ;
190
+ "text/markdown; charset=utf-8; template=handlebars"
191
+ ) ]
192
+ #[ test_case(
193
+ ( "text/plain; charset=utf-8" , ContentType :: Plain ) ;
194
+ "text/plain; charset=utf-8"
195
+ ) ]
196
+ #[ test_case(
197
+ ( "text/plain; charset=utf-8; template=handlebars" , ContentType :: PlainHandlebars ) ;
198
+ "text/plain; charset=utf-8; template=handlebars"
199
+ ) ]
200
+ fn content_type_string_test ( ( raw_str, variant) : ( & str , ContentType ) ) {
201
+ // from str
202
+ assert_eq ! ( ContentType :: from_str( raw_str) . unwrap( ) , variant) ;
203
+
204
+ // parsing
205
+ assert_eq ! ( raw_str. parse:: <ContentType >( ) . unwrap( ) , variant) ;
206
+
207
+ // decoding from cbor
208
+ let mut e = Encoder :: new ( vec ! [ ] ) ;
209
+ e. str ( raw_str) . unwrap ( ) ;
210
+ let bytes = e. into_writer ( ) . clone ( ) ;
211
+ let mut decoder = Decoder :: new ( bytes. as_slice ( ) ) ;
212
+
213
+ assert_eq ! ( ContentType :: decode( & mut decoder, & mut ( ) ) . unwrap( ) , variant) ;
214
+ }
215
+
216
+ #[ test_case(
217
+ ( & [ 0x00 ] , ContentType :: Plain ) ;
218
+ "text/plain; charset=utf-8"
219
+ ) ]
220
+ #[ test_case(
221
+ ( & [ 0x18 , 0x32 ] , ContentType :: Json ) ;
222
+ "application/json"
223
+ ) ]
224
+ #[ test_case(
225
+ ( & [ 0x18 , 0x3C ] , ContentType :: Cbor ) ;
226
+ "application/cbor"
227
+ ) ]
228
+ #[ test_case(
229
+ ( & [ 0x19 , 0x4E , 0x20 ] , ContentType :: Css ) ;
230
+ "text/css; charset=utf-8"
231
+ ) ]
232
+ fn cbor_coap_decoding_test ( ( coap_code_bytes, variant) : ( & [ u8 ] , ContentType ) ) {
233
+ let mut decoder = Decoder :: new ( coap_code_bytes) ;
234
+ assert_eq ! ( ContentType :: decode( & mut decoder, & mut ( ) ) . unwrap( ) , variant) ;
235
+ }
236
+
237
+ #[ test_case(
238
+ & [ 0x13 ] ;
239
+ "application/ace+cbor"
240
+ ) ]
241
+ fn cbor_unsupported_coap_decoding_test ( coap_code_bytes : & [ u8 ] ) {
242
+ let mut decoder = Decoder :: new ( coap_code_bytes) ;
243
+ assert ! ( ContentType :: decode( & mut decoder, & mut ( ) ) . is_err( ) ) ;
153
244
}
154
245
}
0 commit comments