@@ -7,7 +7,10 @@ use super::{CATEGORY_DOCUMENT_UUID_TYPE, PROPOSAL_TEMPLATE_UUID_TYPE};
77use crate :: {
88 error:: CatalystSignedDocError ,
99 metadata:: { ContentEncoding , ContentType } ,
10- validator:: { utils:: validate_provided_doc, ValidationDataProvider } ,
10+ validator:: {
11+ utils:: validate_provided_doc, StatefullRule , StatelessRule , ValidationDataProvider ,
12+ Validator ,
13+ } ,
1114 CatalystSignedDocument , DocumentRef ,
1215} ;
1316
@@ -23,105 +26,139 @@ pub struct ProposalDocument {
2326 category : Option < DocumentRef > ,
2427}
2528
26- impl ProposalDocument {
27- /// Try to build `ProposalDocument` from `CatalystSignedDoc` doing all necessary
28- /// stateless verifications,
29- pub ( crate ) fn from_signed_doc (
30- doc : & CatalystSignedDocument , report : & ProblemReport ,
31- ) -> anyhow:: Result < Self > {
32- let mut failed = false ;
29+ impl Validator for ProposalDocument {
30+ const STATEFULL_RULES : & [ StatefullRule < Self > ] = & [ template_full_check, category_full_check] ;
31+ const STATELESS_RULES : & [ StatelessRule ] = & [
32+ type_check,
33+ content_type_check,
34+ content_encoding_check,
35+ template_check,
36+ ] ;
37+ }
3338
34- if doc. doc_type ( ) . uuid ( ) != PROPOSAL_DOCUMENT_UUID_TYPE {
35- report. invalid_value (
36- "type" ,
37- doc. doc_type ( ) . to_string ( ) . as_str ( ) ,
38- PROPOSAL_DOCUMENT_UUID_TYPE . to_string ( ) . as_str ( ) ,
39- "Invalid Proposal Document type UUID value" ,
40- ) ;
41- failed = true ;
42- }
39+ /// `type` field validation
40+ fn type_check ( doc : & CatalystSignedDocument , report : & ProblemReport ) -> bool {
41+ if doc. doc_type ( ) . uuid ( ) != PROPOSAL_DOCUMENT_UUID_TYPE {
42+ report. invalid_value (
43+ "type" ,
44+ doc. doc_type ( ) . to_string ( ) . as_str ( ) ,
45+ PROPOSAL_DOCUMENT_UUID_TYPE . to_string ( ) . as_str ( ) ,
46+ "Invalid Proposal Document type UUID value" ,
47+ ) ;
48+ return false ;
49+ }
50+ true
51+ }
4352
44- if doc. doc_content_type ( ) != ContentType :: Json {
45- report. invalid_value (
46- "content-type" ,
47- doc. doc_content_type ( ) . to_string ( ) . as_str ( ) ,
48- ContentType :: Json . to_string ( ) . as_str ( ) ,
49- "Invalid Proposal Document content-type value" ,
50- ) ;
51- failed = true ;
52- }
53+ /// `content-type` validation
54+ fn content_type_check ( doc : & CatalystSignedDocument , report : & ProblemReport ) -> bool {
55+ if doc. doc_content_type ( ) != ContentType :: Json {
56+ report. invalid_value (
57+ "content-type" ,
58+ doc. doc_content_type ( ) . to_string ( ) . as_str ( ) ,
59+ ContentType :: Json . to_string ( ) . as_str ( ) ,
60+ "Invalid Proposal Document content-type value" ,
61+ ) ;
62+ return false ;
63+ }
64+ true
65+ }
5366
54- if let Some ( content_encoding) = doc. doc_content_encoding ( ) {
55- if content_encoding != ContentEncoding :: Brotli {
56- report. invalid_value (
57- "content-encoding" ,
58- content_encoding. to_string ( ) . as_str ( ) ,
59- ContentEncoding :: Brotli . to_string ( ) . as_str ( ) ,
60- "Invalid Proposal Document content-encoding value" ,
61- ) ;
62- failed = true ;
63- }
64- } else {
65- report. missing_field (
67+ /// `content-encoding` validation
68+ fn content_encoding_check ( doc : & CatalystSignedDocument , report : & ProblemReport ) -> bool {
69+ if let Some ( content_encoding) = doc. doc_content_encoding ( ) {
70+ if content_encoding != ContentEncoding :: Brotli {
71+ report. invalid_value (
6672 "content-encoding" ,
67- "Proposal Document must have a content-encoding field" ,
73+ content_encoding. to_string ( ) . as_str ( ) ,
74+ ContentEncoding :: Brotli . to_string ( ) . as_str ( ) ,
75+ "Invalid Proposal Document content-encoding value" ,
6876 ) ;
69- failed = true ;
77+ return false ;
7078 }
79+ } else {
80+ report. missing_field (
81+ "content-encoding" ,
82+ "Proposal Document must have a content-encoding field" ,
83+ ) ;
84+ return false ;
85+ }
86+ true
87+ }
7188
72- let category = doc. doc_meta ( ) . category_id ( ) ;
89+ /// `template` validation
90+ fn template_check ( doc : & CatalystSignedDocument , report : & ProblemReport ) -> bool {
91+ if doc. doc_meta ( ) . template ( ) . is_none ( ) {
92+ report. missing_field ( "template" , "Proposal Document must have a template field" ) ;
93+ return false ;
94+ }
95+ true
96+ }
7397
74- let Some ( template) = doc. doc_meta ( ) . template ( ) else {
75- report. missing_field (
98+ /// `template` statefull validation
99+ fn template_full_check (
100+ doc : & ProposalDocument , provider : & dyn ValidationDataProvider , report : & ProblemReport ,
101+ ) -> bool {
102+ let template_validator = |template_doc : CatalystSignedDocument | {
103+ if template_doc. doc_type ( ) . uuid ( ) != PROPOSAL_TEMPLATE_UUID_TYPE {
104+ report. invalid_value (
76105 "template" ,
77- "Proposal Document must have a template
78- field" ,
106+ template_doc. doc_type ( ) . to_string ( ) . as_str ( ) ,
107+ PROPOSAL_TEMPLATE_UUID_TYPE . to_string ( ) . as_str ( ) ,
108+ "Invalid referenced template document type" ,
79109 ) ;
80- anyhow:: bail!( "Failed to build `ProposalDocument` from `CatalystSignedDoc`" ) ;
81- } ;
82-
83- if failed {
84- anyhow:: bail!( "Failed to build `ProposalDocument` from `CatalystSignedDoc`" ) ;
110+ return false ;
85111 }
112+ true
113+ } ;
114+ validate_provided_doc (
115+ & doc. template ,
116+ "Proposal Template" ,
117+ provider,
118+ report,
119+ template_validator,
120+ )
121+ }
86122
87- Ok ( Self { template, category } )
88- }
89-
90- /// A comprehensive statefull validation of the `ProposalDocument` content.
91- pub ( crate ) fn validate_with_report (
92- & self , provider : & impl ValidationDataProvider , report : & ProblemReport ,
93- ) {
94- let template_validator = |template_doc : CatalystSignedDocument | {
95- if template_doc. doc_type ( ) . uuid ( ) != PROPOSAL_TEMPLATE_UUID_TYPE {
123+ /// `category_id` statefull validation
124+ fn category_full_check (
125+ doc : & ProposalDocument , provider : & dyn ValidationDataProvider , report : & ProblemReport ,
126+ ) -> bool {
127+ if let Some ( category) = & doc. category {
128+ let category_validator = |category_doc : CatalystSignedDocument | -> bool {
129+ if category_doc. doc_type ( ) . uuid ( ) != CATEGORY_DOCUMENT_UUID_TYPE {
96130 report. invalid_value (
97- "template " ,
98- template_doc . doc_type ( ) . to_string ( ) . as_str ( ) ,
99- PROPOSAL_TEMPLATE_UUID_TYPE . to_string ( ) . as_str ( ) ,
100- "Invalid referenced template document type" ,
131+ "category_id " ,
132+ category_doc . doc_type ( ) . to_string ( ) . as_str ( ) ,
133+ CATEGORY_DOCUMENT_UUID_TYPE . to_string ( ) . as_str ( ) ,
134+ "Invalid referenced category document type" ,
101135 ) ;
136+ return false ;
102137 }
138+ true
103139 } ;
104- validate_provided_doc (
105- & self . template ,
106- "Proposal Template" ,
107- provider,
108- report,
109- template_validator,
110- ) ;
140+ return validate_provided_doc ( category, "Category" , provider, report, category_validator) ;
141+ }
142+ true
143+ }
111144
112- if let Some ( category) = & self . category {
113- let category_validator = |category_doc : CatalystSignedDocument | {
114- if category_doc. doc_type ( ) . uuid ( ) != CATEGORY_DOCUMENT_UUID_TYPE {
115- report. invalid_value (
116- "category_id" ,
117- category_doc. doc_type ( ) . to_string ( ) . as_str ( ) ,
118- CATEGORY_DOCUMENT_UUID_TYPE . to_string ( ) . as_str ( ) ,
119- "Invalid referenced category document type" ,
120- ) ;
121- }
122- } ;
123- validate_provided_doc ( category, "Category" , provider, report, category_validator) ;
145+ impl ProposalDocument {
146+ /// Try to build `ProposalDocument` from `CatalystSignedDoc` doing all necessary
147+ /// stateless verifications,
148+ pub ( crate ) fn from_signed_doc (
149+ doc : & CatalystSignedDocument , report : & ProblemReport ,
150+ ) -> anyhow:: Result < Self > {
151+ if Self :: stateless_validation ( doc, report) {
152+ anyhow:: bail!( "Failed to build `ProposalDocument` from `CatalystSignedDoc`" ) ;
124153 }
154+
155+ let category = doc. doc_meta ( ) . category_id ( ) ;
156+ let template = doc
157+ . doc_meta ( )
158+ . template ( )
159+ . ok_or ( anyhow:: anyhow!( "missing `template` field" ) ) ?;
160+
161+ Ok ( Self { template, category } )
125162 }
126163}
127164
0 commit comments