Skip to content

Commit 955e788

Browse files
committed
added new catalyst-signed-doc-spec crate
1 parent 6e806ce commit 955e788

File tree

5 files changed

+181
-0
lines changed

5 files changed

+181
-0
lines changed

rust/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ members = [
1616
"vote-tx-v2",
1717
"signed_doc",
1818
"catalyst-signed-doc-macro",
19+
"catalyst-signed-doc-spec",
1920
"rbac-registration",
2021
]
2122

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
[package]
2+
name = "catalyst-signed-doc-spec"
3+
version = "0.0.1"
4+
edition.workspace = true
5+
authors.workspace = true
6+
homepage.workspace = true
7+
repository.workspace = true
8+
license.workspace = true
9+
10+
[dependencies]
11+
serde_json = "1.0.142"
12+
anyhow = "1.0.99"
13+
serde = { version = "1.0.219", features = ["derive"] }
14+
15+
quote = "1.0"
16+
proc-macro2 = "1.0"
17+
18+
[lints]
19+
workspace = true
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
//! `signed_doc.json` headers content type field JSON definition
2+
3+
/// `signed_doc.json` "content type" field JSON object
4+
#[derive(serde::Deserialize)]
5+
#[allow(clippy::missing_docs_in_private_items)]
6+
pub struct ContentType {
7+
pub required: super::IsRequired,
8+
pub value: Option<String>,
9+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
//! `signed_doc.json` "ref" field JSON definition
2+
3+
use crate::{DocTypes, IsRequired};
4+
5+
/// `signed_doc.json` "ref" field JSON object
6+
#[derive(serde::Deserialize)]
7+
pub struct Ref {
8+
pub required: IsRequired,
9+
#[serde(rename = "type")]
10+
pub doc_type: DocTypes,
11+
pub multiple: Option<bool>,
12+
}
Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
//! Catalyst Signed Document spec type
2+
3+
#![allow(missing_docs, clippy::missing_docs_in_private_items)]
4+
5+
pub mod content_type;
6+
pub mod doc_ref;
7+
8+
use std::{collections::HashMap, ops::Deref};
9+
10+
/// Catalyst Signed Document spec representation struct
11+
#[derive(serde::Deserialize)]
12+
pub struct CatalystSignedDocSpec {
13+
/// A collection of document's supported content types
14+
#[serde(rename = "contentTypes")]
15+
#[allow(dead_code)]
16+
pub content_types: HashMap<String, ContentTypeSpec>,
17+
/// A collection of document's specs
18+
pub docs: HashMap<DocumentName, DocSpec>,
19+
}
20+
21+
/// Catalyst Signed Document supported content type declaration struct
22+
#[derive(serde::Deserialize)]
23+
pub struct ContentTypeSpec {
24+
/// CoAP Content-Formats
25+
#[allow(dead_code)]
26+
coap_type: Option<u32>,
27+
}
28+
29+
// A thin wrapper over the string document name values
30+
#[derive(serde::Deserialize, PartialEq, Eq, Hash)]
31+
pub struct DocumentName(String);
32+
33+
impl DocumentName {
34+
/// returns document name
35+
pub fn name(&self) -> &str {
36+
&self.0
37+
}
38+
39+
/// returns a document name as a `Ident` in the following form
40+
/// `"PROPOSAL_FORM_TEMPLATE"`
41+
pub fn ident(&self) -> proc_macro2::Ident {
42+
quote::format_ident!(
43+
"{}",
44+
self.0
45+
.split_whitespace()
46+
.map(str::to_uppercase)
47+
.collect::<Vec<_>>()
48+
.join("_")
49+
)
50+
}
51+
}
52+
53+
/// Specific document type definition
54+
#[derive(serde::Deserialize)]
55+
pub struct DocSpec {
56+
/// Document type UUID v4 value
57+
#[serde(rename = "type")]
58+
pub doc_type: String,
59+
/// `headers` field
60+
pub headers: Headers,
61+
/// Document type metadata definitions
62+
pub metadata: Metadata,
63+
}
64+
65+
/// Document's metadata fields definition
66+
#[derive(serde::Deserialize)]
67+
#[allow(clippy::missing_docs_in_private_items)]
68+
pub struct Metadata {
69+
#[serde(rename = "ref")]
70+
pub doc_ref: doc_ref::Ref,
71+
}
72+
73+
/// Document's metadata fields definition
74+
#[derive(serde::Deserialize)]
75+
#[allow(clippy::missing_docs_in_private_items)]
76+
pub struct Headers {
77+
#[serde(rename = "content type")]
78+
pub content_type: content_type::ContentType,
79+
}
80+
81+
/// "required" field definition
82+
#[derive(serde::Deserialize)]
83+
#[serde(rename_all = "lowercase")]
84+
#[allow(clippy::missing_docs_in_private_items)]
85+
pub enum IsRequired {
86+
Yes,
87+
Excluded,
88+
Optional,
89+
}
90+
91+
/// A helper type for deserialization "type" metadata field
92+
pub struct DocTypes(Vec<DocumentName>);
93+
94+
impl Deref for DocTypes {
95+
type Target = Vec<DocumentName>;
96+
97+
fn deref(&self) -> &Self::Target {
98+
&self.0
99+
}
100+
}
101+
102+
impl<'de> serde::Deserialize<'de> for DocTypes {
103+
#[allow(clippy::missing_docs_in_private_items)]
104+
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
105+
where D: serde::Deserializer<'de> {
106+
#[derive(serde::Deserialize)]
107+
#[serde(untagged)]
108+
enum SingleOrVec {
109+
Single(DocumentName),
110+
Multiple(Vec<DocumentName>),
111+
}
112+
let value = Option::<SingleOrVec>::deserialize(deserializer)?;
113+
let result = match value {
114+
Some(SingleOrVec::Single(item)) => vec![item],
115+
Some(SingleOrVec::Multiple(items)) => items,
116+
None => vec![],
117+
};
118+
Ok(Self(result))
119+
}
120+
}
121+
122+
impl CatalystSignedDocSpec {
123+
/// Loading a Catalyst Signed Documents spec from the `signed_doc.json`
124+
pub fn load_signed_doc_spec() -> anyhow::Result<CatalystSignedDocSpec> {
125+
let signed_doc_str = include_str!("../../../specs/signed_doc.json");
126+
let signed_doc_spec = serde_json::from_str(signed_doc_str)
127+
.map_err(|e| anyhow::anyhow!("Invalid Catalyst Signed Documents JSON Spec: {e}"))?;
128+
Ok(signed_doc_spec)
129+
}
130+
}
131+
132+
#[cfg(test)]
133+
mod tests {
134+
use super::*;
135+
136+
#[test]
137+
fn load_signed_doc_spec_test() {
138+
assert!(CatalystSignedDocSpec::load_signed_doc_spec().is_ok());
139+
}
140+
}

0 commit comments

Comments
 (0)