@@ -10,7 +10,7 @@ use std::string::{String, ToString};
1010
1111use http:: Uri ;
1212use ngx:: collections:: Vec ;
13- use serde:: { Deserialize , Serialize } ;
13+ use serde:: { de :: IgnoredAny , Deserialize , Serialize } ;
1414
1515use crate :: conf:: identifier:: Identifier ;
1616
@@ -22,6 +22,8 @@ pub struct DirectoryMetadata {
2222 pub website : Option < Uri > ,
2323 pub caa_identities : Option < Vec < String > > ,
2424 pub external_account_required : Option < bool > ,
25+ #[ serde( deserialize_with = "deserialize_null_as_default" ) ]
26+ pub profiles : std:: collections:: BTreeMap < String , IgnoredAny > ,
2527}
2628
2729/// RFC8555 Section 7.1.1 Directory
@@ -118,6 +120,8 @@ pub struct OrderRequest<'a> {
118120 pub not_before : Option < String > ,
119121 #[ serde( skip_serializing_if = "Option::is_none" ) ]
120122 pub not_after : Option < String > ,
123+ #[ serde( skip_serializing_if = "Option::is_none" ) ]
124+ pub profile : Option < & ' a str > ,
121125}
122126
123127#[ derive( Clone , Debug , Deserialize , Eq , PartialEq ) ]
@@ -200,6 +204,7 @@ pub enum ErrorKind {
200204 ExternalAccountRequired ,
201205 IncorrectResponse ,
202206 InvalidContact ,
207+ InvalidProfile ,
203208 Malformed ,
204209 OrderNotReady ,
205210 RateLimited ,
@@ -232,6 +237,7 @@ const ERROR_KIND: &[(&str, ErrorKind)] = &[
232237 ) ,
233238 ( "incorrectResponse" , ErrorKind :: IncorrectResponse ) ,
234239 ( "invalidContact" , ErrorKind :: InvalidContact ) ,
240+ ( "invalidProfile" , ErrorKind :: InvalidProfile ) ,
235241 ( "malformed" , ErrorKind :: Malformed ) ,
236242 ( "orderNotReady" , ErrorKind :: OrderNotReady ) ,
237243 ( "rateLimited" , ErrorKind :: RateLimited ) ,
@@ -336,6 +342,7 @@ impl Problem {
336342
337343 ErrorKind :: BadCsr
338344 | ErrorKind :: Caa
345+ | ErrorKind :: InvalidProfile
339346 | ErrorKind :: RejectedIdentifier
340347 | ErrorKind :: UnsupportedIdentifier => ProblemCategory :: Order ,
341348
@@ -346,6 +353,18 @@ impl Problem {
346353 }
347354}
348355
356+ /// Deserializes value of type T, while handling explicit `null` as a Default.
357+ ///
358+ /// This helper complements `#[serde(default)]`, which only works on omitted fields.
359+ fn deserialize_null_as_default < ' de , D , T > ( deserializer : D ) -> Result < T , D :: Error >
360+ where
361+ D : serde:: de:: Deserializer < ' de > ,
362+ T : serde:: de:: Deserialize < ' de > + Default ,
363+ {
364+ let val = Option :: < T > :: deserialize ( deserializer) ?;
365+ Ok ( val. unwrap_or_default ( ) )
366+ }
367+
349368fn deserialize_vec_of_uri < ' de , D > ( deserializer : D ) -> Result < Vec < Uri > , D :: Error >
350369where
351370 D : serde:: Deserializer < ' de > ,
@@ -385,6 +404,62 @@ where
385404mod tests {
386405 use super :: * ;
387406
407+ #[ test]
408+ fn directory ( ) {
409+ // complete example
410+ let _: Directory = serde_json:: from_str (
411+ r#"{
412+ "newNonce": "https://example.com/acme/new-nonce",
413+ "newAccount": "https://example.com/acme/new-account",
414+ "newOrder": "https://example.com/acme/new-order",
415+ "newAuthz": "https://example.com/acme/new-authz",
416+ "revokeCert": "https://example.com/acme/revoke-cert",
417+ "keyChange": "https://example.com/acme/key-change",
418+ "meta": {
419+ "termsOfService": "https://example.com/acme/terms/2017-5-30",
420+ "website": "https://www.example.com/",
421+ "caaIdentities": ["example.com"],
422+ "externalAccountRequired": false,
423+ "profiles": {
424+ "profile1": "https://example.com/acme/docs/profiles#profile1",
425+ "profile2": "https://example.com/acme/docs/profiles#profile2"
426+ }
427+ }
428+ }"# ,
429+ )
430+ . unwrap ( ) ;
431+
432+ // minimal
433+ let _: Directory = serde_json:: from_str (
434+ r#"{
435+ "newNonce": "https://example.com/acme/new-nonce",
436+ "newAccount": "https://example.com/acme/new-account",
437+ "newOrder": "https://example.com/acme/new-order"
438+ }"# ,
439+ )
440+ . unwrap ( ) ;
441+
442+ // null
443+ let _: Directory = serde_json:: from_str (
444+ r#"{
445+ "newNonce": "https://example.com/acme/new-nonce",
446+ "newAccount": "https://example.com/acme/new-account",
447+ "newOrder": "https://example.com/acme/new-order",
448+ "newAuthz": null,
449+ "revokeCert": null,
450+ "keyChange": null,
451+ "meta": {
452+ "termsOfService": null,
453+ "website": null,
454+ "caaIdentities": null,
455+ "externalAccountRequired": null,
456+ "profiles": null
457+ }
458+ }"# ,
459+ )
460+ . unwrap ( ) ;
461+ }
462+
388463 #[ test]
389464 fn order ( ) {
390465 let _order: Order = serde_json:: from_str (
0 commit comments