11//! Document Payload Content Type.
22
3- use std:: {
4- fmt:: { Display , Formatter } ,
5- str:: FromStr ,
6- } ;
3+ use std:: { str:: FromStr , string:: ToString } ;
74
85use strum:: VariantArray ;
96
107/// Payload Content Type.
11- #[ derive( Debug , Copy , Clone , PartialEq , Eq , VariantArray ) ]
8+ #[ derive( Debug , Copy , Clone , PartialEq , Eq , VariantArray , strum_macros :: Display ) ]
129pub enum ContentType {
1310 /// `application/cbor`
11+ #[ strum( to_string = "application/cbor" ) ]
1412 Cbor ,
1513 /// `application/cddl`
14+ #[ strum( to_string = "application/cddl" ) ]
1615 Cddl ,
1716 /// `application/json`
17+ #[ strum( to_string = "application/json" ) ]
1818 Json ,
1919 /// `application/json+schema`
20+ #[ strum( to_string = "application/json+schema" ) ]
2021 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 ,
3546}
3647
3748impl FromStr for ContentType {
@@ -43,6 +54,14 @@ impl FromStr for ContentType {
4354 "application/cddl" => Ok ( Self :: Cddl ) ,
4455 "application/json" => Ok ( Self :: Json ) ,
4556 "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 ) ,
4665 _ => {
4766 anyhow:: bail!(
4867 "Unsupported Content Type: {s:?}, Supported only: {:?}" ,
@@ -56,6 +75,25 @@ impl FromStr for ContentType {
5675 }
5776}
5877
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+
5997impl < ' de > serde:: Deserialize < ' de > for ContentType {
6098 fn deserialize < D > ( deserializer : D ) -> Result < Self , D :: Error >
6199 where D : serde:: Deserializer < ' de > {
@@ -94,61 +132,114 @@ impl minicbor::Decode<'_, ()> for ContentType {
94132 _ctx : & mut ( ) ,
95133 ) -> Result < Self , minicbor:: decode:: Error > {
96134 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)
111141 }
112142 }
113143}
114144
115145#[ cfg( test) ]
116146mod tests {
147+ use minicbor:: { Decode , Decoder , Encoder } ;
148+ use test_case:: test_case;
149+
117150 use super :: * ;
118151
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( ) ) ;
153244 }
154245}
0 commit comments