11//! Catalyst Signed Document spec type
22
3+ // cspell: words pascalcase
4+
5+ pub ( crate ) mod content_type;
36pub ( crate ) mod doc_ref;
47
58use std:: { collections:: HashMap , ops:: Deref } ;
69
10+ use inflector:: cases:: pascalcase:: to_pascal_case;
711use proc_macro2:: Ident ;
812use quote:: format_ident;
913
1014/// Catalyst Signed Document spec representation struct
1115#[ derive( serde:: Deserialize ) ]
1216pub ( crate ) struct CatalystSignedDocSpec {
17+ /// A collection of document's supported content types
18+ #[ serde( rename = "contentTypes" ) ]
19+ #[ allow( dead_code) ]
20+ pub ( crate ) content_types : HashMap < ContentTypeTemplate , ContentTypeSpec > ,
1321 /// A collection of document's specs
1422 pub ( crate ) docs : HashMap < DocumentName , DocSpec > ,
1523}
1624
25+ // A thin wrapper over the RFC2046 content type strings.
26+ #[ derive( serde:: Deserialize , PartialEq , Eq , Hash ) ]
27+ pub ( crate ) struct ContentTypeTemplate ( pub ( crate ) String ) ;
28+
29+ impl ContentTypeTemplate {
30+ /// returns a content type template as a `Ident` in the following form.
31+ ///
32+ /// text/css; charset=utf-8; template=handlebars
33+ /// => `CssHandlebars`
34+ ///
35+ /// text/css; charset=utf-8
36+ /// => `Css`
37+ pub ( crate ) fn ident ( & self ) -> Ident {
38+ let raw = self . 0 . as_str ( ) ;
39+
40+ // split into parts like "text/css; charset=utf-8; template=handlebars"
41+ let mut parts = raw. split ( ';' ) . map ( str:: trim) ;
42+
43+ // first part is "type/subtype"
44+ let first = parts. next ( ) . unwrap_or_default ( ) ; // e.g. "text/css"
45+ let subtype = first. split ( '/' ) . nth ( 1 ) . unwrap_or_default ( ) ; // "css"
46+
47+ // look for "template=..."
48+ let template = parts
49+ . find_map ( |p| p. strip_prefix ( "template=" ) )
50+ . map ( to_pascal_case) ;
51+
52+ // build PascalCase
53+ let mut ident = String :: new ( ) ;
54+ ident. push_str ( & to_pascal_case ( subtype) ) ;
55+ if let Some ( t) = template {
56+ ident. push_str ( & t) ;
57+ }
58+
59+ format_ident ! ( "{}" , ident)
60+ }
61+ }
62+
63+ /// Catalyst Signed Document supported content type declaration struct
64+ #[ derive( serde:: Deserialize ) ]
65+ pub ( crate ) struct ContentTypeSpec {
66+ /// CoAP Content-Formats
67+ #[ allow( dead_code) ]
68+ coap_type : Option < u32 > ,
69+ }
70+
1771// A thin wrapper over the string document name values
1872#[ derive( serde:: Deserialize , PartialEq , Eq , Hash ) ]
1973pub ( crate ) struct DocumentName ( String ) ;
@@ -44,7 +98,8 @@ pub(crate) struct DocSpec {
4498 /// Document type UUID v4 value
4599 #[ serde( rename = "type" ) ]
46100 pub ( crate ) doc_type : String ,
47-
101+ /// `headers` field
102+ pub ( crate ) headers : Headers ,
48103 /// Document type metadata definitions
49104 pub ( crate ) metadata : Metadata ,
50105}
@@ -57,6 +112,14 @@ pub(crate) struct Metadata {
57112 pub ( crate ) doc_ref : doc_ref:: Ref ,
58113}
59114
115+ /// Document's metadata fields definition
116+ #[ derive( serde:: Deserialize ) ]
117+ #[ allow( clippy:: missing_docs_in_private_items) ]
118+ pub ( crate ) struct Headers {
119+ #[ serde( rename = "content type" ) ]
120+ pub ( crate ) content_type : content_type:: ContentType ,
121+ }
122+
60123/// "required" field definition
61124#[ derive( serde:: Deserialize ) ]
62125#[ serde( rename_all = "lowercase" ) ]
@@ -103,7 +166,8 @@ impl CatalystSignedDocSpec {
103166 // #[allow(dependency_on_unit_never_type_fallback)]
104167 pub ( crate ) fn load_signed_doc_spec ( ) -> anyhow:: Result < CatalystSignedDocSpec > {
105168 let signed_doc_str = include_str ! ( "../../../../specs/signed_doc.json" ) ;
106- let signed_doc_spec = serde_json:: from_str ( signed_doc_str) ?;
169+ let signed_doc_spec = serde_json:: from_str ( signed_doc_str)
170+ . map_err ( |e| anyhow:: anyhow!( "Invalid Catalyst Signed Documents JSON Spec: {e}" ) ) ?;
107171 Ok ( signed_doc_spec)
108172 }
109173}
0 commit comments