Skip to content

Commit eb5e0d9

Browse files
authored
feat(rust/signed-doc): RefRule auto generation (#525)
* add `ref_rule` * wip * wip * fix clippy * fix spelling * fix
1 parent 7ac4e5b commit eb5e0d9

File tree

4 files changed

+113
-7
lines changed

4 files changed

+113
-7
lines changed
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
//! `RefRule` generation
2+
3+
use proc_macro2::TokenStream;
4+
use quote::quote;
5+
6+
use crate::signed_doc_spec::{self, IsRequired};
7+
8+
/// Generating `RefRule` instantiation
9+
pub(crate) fn ref_rule(ref_spec: &signed_doc_spec::doc_ref::Ref) -> anyhow::Result<TokenStream> {
10+
let optional = match ref_spec.required {
11+
IsRequired::Yes => false,
12+
IsRequired::Optional => true,
13+
IsRequired::Excluded => {
14+
return Ok(quote! {
15+
crate::validator::rules::RefRule::NotSpecified
16+
});
17+
},
18+
};
19+
20+
anyhow::ensure!(!ref_spec.doc_type.is_empty(), "'type' field should exists and has at least one entry for the required 'ref' metadata definition");
21+
22+
let const_type_name_idents = ref_spec.doc_type.iter().map(|doc_name| {
23+
let const_type_name_ident = doc_name.ident();
24+
quote! {
25+
crate::doc_types::#const_type_name_ident
26+
}
27+
});
28+
let multiple = ref_spec.multiple.ok_or(anyhow::anyhow!(
29+
"'multiple' field should exists for the required 'ref' metadata definition"
30+
))?;
31+
Ok(quote! {
32+
crate::validator::rules::RefRule::Specified {
33+
exp_ref_types: vec![ #(#const_type_name_idents,)* ],
34+
multiple: #multiple,
35+
optional: #optional,
36+
}
37+
})
38+
}

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

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
//! `catalyst_signed_documents_rules!` macro implementation
22
3+
pub(crate) mod doc_ref;
4+
35
use proc_macro2::TokenStream;
46
use quote::quote;
57

@@ -10,9 +12,10 @@ pub(crate) fn catalyst_signed_documents_rules_impl() -> anyhow::Result<TokenStre
1012
let spec = CatalystSignedDocSpec::load_signed_doc_spec()?;
1113

1214
let mut rules_definitions = Vec::new();
13-
for (doc_name, _doc_spec) in spec.docs {
15+
for (doc_name, doc_spec) in spec.docs {
1416
let const_type_name_ident = doc_name.ident();
1517

18+
let ref_rule = doc_ref::ref_rule(&doc_spec.metadata.doc_ref)?;
1619
// TODO: implement a proper initialization for all specific validation rules
1720
let rules = quote! {
1821
crate::validator::rules::Rules {
@@ -27,7 +30,7 @@ pub(crate) fn catalyst_signed_documents_rules_impl() -> anyhow::Result<TokenStre
2730
},
2831
content: crate::validator::rules::ContentRule::NotSpecified,
2932
parameters: crate::validator::rules::ParametersRule::NotSpecified,
30-
doc_ref: crate::validator::rules::RefRule::NotSpecified,
33+
doc_ref: #ref_rule,
3134
reply: crate::validator::rules::ReplyRule::NotSpecified,
3235
section: crate::validator::rules::SectionRule::NotSpecified,
3336
kid: crate::validator::rules::SignatureKidRule {
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
//! `signed_doc.json` "ref" field JSON definition
2+
3+
use crate::signed_doc_spec::{DocTypes, IsRequired};
4+
5+
/// `signed_doc.json` "ref" field JSON object
6+
#[derive(serde::Deserialize)]
7+
#[allow(clippy::missing_docs_in_private_items)]
8+
pub(crate) struct Ref {
9+
pub(crate) required: IsRequired,
10+
#[serde(rename = "type")]
11+
pub(crate) doc_type: DocTypes,
12+
pub(crate) multiple: Option<bool>,
13+
}

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

Lines changed: 57 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
//! Catalyst Signed Document spec type
22
3-
use std::collections::HashMap;
3+
pub(crate) mod doc_ref;
4+
5+
use std::{collections::HashMap, ops::Deref};
46

5-
use anyhow::Context;
67
use proc_macro2::Ident;
78
use quote::format_ident;
89

@@ -43,15 +44,66 @@ pub(crate) struct DocSpec {
4344
/// Document type UUID v4 value
4445
#[serde(rename = "type")]
4546
pub(crate) doc_type: String,
47+
48+
/// Document type metadata definitions
49+
pub(crate) metadata: Metadata,
50+
}
51+
52+
/// Document's metadata fields definition
53+
#[derive(serde::Deserialize)]
54+
#[allow(clippy::missing_docs_in_private_items)]
55+
pub(crate) struct Metadata {
56+
#[serde(rename = "ref")]
57+
pub(crate) doc_ref: doc_ref::Ref,
58+
}
59+
60+
/// "required" field definition
61+
#[derive(serde::Deserialize)]
62+
#[serde(rename_all = "lowercase")]
63+
#[allow(clippy::missing_docs_in_private_items)]
64+
pub(crate) enum IsRequired {
65+
Yes,
66+
Excluded,
67+
Optional,
68+
}
69+
70+
/// A helper type for deserialization "type" metadata field
71+
pub(crate) struct DocTypes(Vec<DocumentName>);
72+
73+
impl Deref for DocTypes {
74+
type Target = Vec<DocumentName>;
75+
76+
fn deref(&self) -> &Self::Target {
77+
&self.0
78+
}
79+
}
80+
81+
impl<'de> serde::Deserialize<'de> for DocTypes {
82+
#[allow(clippy::missing_docs_in_private_items)]
83+
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
84+
where D: serde::Deserializer<'de> {
85+
#[derive(serde::Deserialize)]
86+
#[serde(untagged)]
87+
enum SingleOrVec {
88+
Single(DocumentName),
89+
Multiple(Vec<DocumentName>),
90+
}
91+
let value = Option::<SingleOrVec>::deserialize(deserializer)?;
92+
let result = match value {
93+
Some(SingleOrVec::Single(item)) => vec![item],
94+
Some(SingleOrVec::Multiple(items)) => items,
95+
None => vec![],
96+
};
97+
Ok(Self(result))
98+
}
4699
}
47100

48101
impl CatalystSignedDocSpec {
49102
/// Loading a Catalyst Signed Documents spec from the `signed_doc.json`
50103
// #[allow(dependency_on_unit_never_type_fallback)]
51104
pub(crate) fn load_signed_doc_spec() -> anyhow::Result<CatalystSignedDocSpec> {
52-
let signed_doc_str = include_str!("../../../specs/signed_doc.json");
53-
let signed_doc_spec = serde_json::from_str(signed_doc_str)
54-
.context("Catalyst Signed Documents spec must be a JSON object")?;
105+
let signed_doc_str = include_str!("../../../../specs/signed_doc.json");
106+
let signed_doc_spec = serde_json::from_str(signed_doc_str)?;
55107
Ok(signed_doc_spec)
56108
}
57109
}

0 commit comments

Comments
 (0)