Skip to content

Commit 0a420ca

Browse files
committed
feat: content type from spec
1 parent 19384c5 commit 0a420ca

File tree

7 files changed

+77
-43
lines changed

7 files changed

+77
-43
lines changed

rust/catalyst-signed-doc-macro/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,4 +19,5 @@ quote = "1.0"
1919
proc-macro2 = "1.0"
2020
serde_json = "1.0.142"
2121
anyhow = "1.0.99"
22+
Inflector = "0.11.4"
2223
serde = { version = "1.0.219", features = ["derive"] }
Lines changed: 14 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,20 @@
11
//! `ContentTypeRule` generation
22
3+
use std::collections::HashMap;
4+
35
use proc_macro2::TokenStream;
46
use quote::quote;
57

6-
use crate::signed_doc_spec::{self, IsRequired};
8+
use crate::signed_doc_spec::{self, ContentTypeSpec, ContentTypeTemplate, IsRequired};
79

810
/// Generating `ContentTypeRule` instantiation
911
pub(crate) fn into_rule(
10-
content_type: &signed_doc_spec::content_type::ContentType
12+
content_types: &HashMap<ContentTypeTemplate, ContentTypeSpec>,
13+
field: &signed_doc_spec::content_type::ContentType,
1114
) -> anyhow::Result<TokenStream> {
12-
if matches!(content_type.required, IsRequired::Excluded) {
15+
if matches!(field.required, IsRequired::Excluded) {
1316
anyhow::ensure!(
14-
content_type.value.is_empty(),
17+
field.value.is_empty(),
1518
"'value' field must not exist when 'required' is 'excluded'"
1619
);
1720

@@ -20,42 +23,19 @@ pub(crate) fn into_rule(
2023
});
2124
}
2225

23-
if matches!(content_type.required, IsRequired::Yes) {
24-
anyhow::ensure!(!content_type.value.is_empty(), "'value' field must exist");
26+
if matches!(field.required, IsRequired::Yes) {
27+
anyhow::ensure!(!field.value.is_empty(), "'value' field must exist");
2528
}
2629

27-
let exp = match content_type.value.as_str() {
28-
"application/cbor" => quote! { ContentType::Cbor },
29-
"application/cddl" => quote! { ContentType::Cddl },
30-
"application/json" => quote! { ContentType::Json },
31-
"application/json+schema" => quote! { ContentType::JsonSchema },
32-
"text/css; charset=utf-8" => quote! { ContentType::Css },
33-
"text/css; charset=utf-8; template=handlebars" => {
34-
quote! { ContentType::CssHandlebars }
35-
},
36-
"text/html; charset=utf-8" => quote! { ContentType::Html },
37-
"text/html; charset=utf-8; template=handlebars" => {
38-
quote! { ContentType::HtmlHandlebars }
39-
},
40-
"text/markdown; charset=utf-8" => quote! { ContentType::Markdown },
41-
"text/markdown; charset=utf-8; template=handlebars" => {
42-
quote! { ContentType::MarkdownHandlebars }
43-
},
44-
"text/plain; charset=utf-8" => quote! { ContentType::Plain },
45-
"text/plain; charset=utf-8; template=handlebars" => {
46-
quote! { ContentType::PlainHandlebars }
47-
},
48-
_ => {
49-
return Err(anyhow::anyhow!(
50-
"Unsupported Content Type: {}",
51-
content_type.value
52-
))
53-
},
30+
let template = ContentTypeTemplate(field.value.clone());
31+
let Some(_) = content_types.get(&template) else {
32+
return Err(anyhow::anyhow!("Unsupported Content Type: {}", field.value));
5433
};
34+
let ident = template.ident();
5535

5636
Ok(quote! {
5737
crate::validator::rules::ContentTypeRule::Specified {
58-
exp: #exp,
38+
exp: ContentType::#ident,
5939
}
6040
})
6141
}

rust/catalyst-signed-doc-macro/src/rules/mod.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,8 @@ pub(crate) fn catalyst_signed_documents_rules_impl() -> anyhow::Result<TokenStre
1616
for (doc_name, doc_spec) in spec.docs {
1717
let const_type_name_ident = doc_name.ident();
1818

19-
let content_type_rule = content_type::into_rule(&doc_spec.headers.content_type)?;
19+
let content_type_rule =
20+
content_type::into_rule(&spec.content_types, &doc_spec.headers.content_type)?;
2021
let ref_rule = doc_ref::ref_rule(&doc_spec.metadata.doc_ref)?;
2122
// TODO: implement a proper initialization for all specific validation rules
2223
let rules = quote! {

rust/catalyst-signed-doc-macro/src/signed_doc_spec/mod.rs

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,67 @@ pub(crate) mod doc_ref;
55

66
use std::{collections::HashMap, ops::Deref};
77

8+
use inflector::cases::pascalcase::to_pascal_case;
89
use proc_macro2::Ident;
910
use quote::format_ident;
1011

1112
/// Catalyst Signed Document spec representation struct
1213
#[derive(serde::Deserialize)]
1314
pub(crate) struct CatalystSignedDocSpec {
15+
/// A collection of document's supported content types
16+
#[serde(rename = "contentTypes")]
17+
#[allow(dead_code)]
18+
pub(crate) content_types: HashMap<ContentTypeTemplate, ContentTypeSpec>,
1419
/// A collection of document's specs
1520
pub(crate) docs: HashMap<DocumentName, DocSpec>,
1621
}
1722

23+
// A thin wrapper over the RFC2046 content type strings.
24+
#[derive(serde::Deserialize, PartialEq, Eq, Hash)]
25+
pub(crate) struct ContentTypeTemplate(pub(crate) String);
26+
27+
impl ContentTypeTemplate {
28+
/// returns a document name as a `Ident` in the following form
29+
///
30+
/// text/css; charset=utf-8; template=handlebars
31+
/// => `CssHandlebars`
32+
///
33+
/// text/css; charset=utf-8
34+
/// => `Css`
35+
pub(crate) fn ident(&self) -> Ident {
36+
let raw = self.0.as_str();
37+
38+
// split into parts like "text/css; charset=utf-8; template=handlebars"
39+
let mut parts = raw.split(';').map(str::trim);
40+
41+
// first part is "type/subtype"
42+
let first = parts.next().unwrap_or_default(); // e.g. "text/css"
43+
let subtype = first.split('/').nth(1).unwrap_or_default(); // "css"
44+
45+
// look for "template=..."
46+
let template = parts
47+
.find_map(|p| p.strip_prefix("template="))
48+
.map(to_pascal_case);
49+
50+
// build PascalCase
51+
let mut ident = String::new();
52+
ident.push_str(&to_pascal_case(subtype));
53+
if let Some(t) = template {
54+
ident.push_str(&t);
55+
}
56+
57+
format_ident!("{}", ident)
58+
}
59+
}
60+
61+
/// Catalyst Signed Document supported content type declaration struct
62+
#[derive(serde::Deserialize)]
63+
pub(crate) struct ContentTypeSpec {
64+
/// CoAP Content-Formats
65+
#[allow(dead_code)]
66+
coap_type: Option<u32>,
67+
}
68+
1869
// A thin wrapper over the string document name values
1970
#[derive(serde::Deserialize, PartialEq, Eq, Hash)]
2071
pub(crate) struct DocumentName(String);

rust/signed_doc/src/metadata/content_type.rs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,9 @@ pub enum ContentType {
1616
/// `application/json`
1717
#[strum(to_string = "application/json")]
1818
Json,
19-
/// `application/json+schema`
20-
#[strum(to_string = "application/json+schema")]
21-
JsonSchema,
19+
/// `application/schema+json`
20+
#[strum(to_string = "application/schema+json")]
21+
SchemaJson,
2222
/// `text/css; charset=utf-8`
2323
#[strum(to_string = "text/css; charset=utf-8")]
2424
Css,
@@ -53,7 +53,7 @@ impl FromStr for ContentType {
5353
"application/cbor" => Ok(Self::Cbor),
5454
"application/cddl" => Ok(Self::Cddl),
5555
"application/json" => Ok(Self::Json),
56-
"application/json+schema" => Ok(Self::JsonSchema),
56+
"application/schema+json" => Ok(Self::SchemaJson),
5757
"text/css; charset=utf-8" => Ok(Self::Css),
5858
"text/css; charset=utf-8; template=handlebars" => Ok(Self::CssHandlebars),
5959
"text/html; charset=utf-8" => Ok(Self::Html),
@@ -162,8 +162,8 @@ mod tests {
162162
"application/json"
163163
)]
164164
#[test_case(
165-
("application/json+schema", ContentType::JsonSchema);
166-
"application/json+schema"
165+
("application/schema+json", ContentType::SchemaJson);
166+
"application/schema+json"
167167
)]
168168
#[test_case(
169169
("text/css; charset=utf-8", ContentType::Css);

rust/signed_doc/src/validator/rules/content.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ impl ContentRule {
7373
ContentType::Json => templated_json_schema_check(doc, template_doc),
7474
ContentType::Cddl
7575
| ContentType::Cbor
76-
| ContentType::JsonSchema
76+
| ContentType::SchemaJson
7777
| ContentType::Css
7878
| ContentType::CssHandlebars
7979
| ContentType::Html

rust/signed_doc/src/validator/rules/content_type.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ pub(crate) enum ContentTypeRule {
1111
exp: ContentType,
1212
},
1313
/// Content Type field must not be present in the document.
14+
#[allow(dead_code)]
1415
NotSpecified,
1516
}
1617

@@ -92,7 +93,7 @@ impl ContentTypeRule {
9293
}
9394
},
9495
ContentType::Cddl
95-
| ContentType::JsonSchema
96+
| ContentType::SchemaJson
9697
| ContentType::Css
9798
| ContentType::CssHandlebars
9899
| ContentType::Html

0 commit comments

Comments
 (0)