33use catalyst_types:: problem_report:: ProblemReport ;
44
55use crate :: {
6- providers:: CatalystSignedDocumentProvider , validator :: utils :: validate_doc_refs ,
7- CatalystSignedDocument , DocType ,
6+ providers:: CatalystSignedDocumentProvider , CatalystSignedDocument , DocType , DocumentRef ,
7+ DocumentRefs ,
88} ;
99
1010/// `ref` field validation rule
@@ -36,11 +36,16 @@ impl RefRule {
3636 optional,
3737 } = self
3838 {
39- if let Some ( doc_ref) = doc. doc_meta ( ) . doc_ref ( ) {
40- let ref_validator = |ref_doc : CatalystSignedDocument | {
41- referenced_doc_check ( & ref_doc, exp_ref_types, "ref" , doc. report ( ) )
42- } ;
43- return validate_doc_refs ( doc_ref, provider, doc. report ( ) , ref_validator) . await ;
39+ if let Some ( doc_refs) = doc. doc_meta ( ) . doc_ref ( ) {
40+ return doc_refs_check (
41+ doc_refs,
42+ exp_ref_types,
43+ "ref" ,
44+ provider,
45+ doc. report ( ) ,
46+ |_| true ,
47+ )
48+ . await ;
4449 } else if !optional {
4550 doc. report ( )
4651 . missing_field ( "ref" , & format ! ( "{context}, document must have ref field" ) ) ;
@@ -62,15 +67,98 @@ impl RefRule {
6267 }
6368}
6469
65- /// A generic implementation of the referenced document validation.
66- pub ( crate ) fn referenced_doc_check (
70+ /// Validate all the document references by the defined validation rules,
71+ /// plus conducting additional validations with the provided `validator`.
72+ /// Document all possible error in doc report (no fail fast)
73+ pub ( crate ) async fn doc_refs_check < Provider , Validator > (
74+ doc_refs : & DocumentRefs ,
75+ exp_ref_types : & [ DocType ] ,
76+ field_name : & str ,
77+ provider : & Provider ,
78+ report : & ProblemReport ,
79+ validator : Validator ,
80+ ) -> anyhow:: Result < bool >
81+ where
82+ Provider : CatalystSignedDocumentProvider ,
83+ Validator : Fn ( & CatalystSignedDocument ) -> bool ,
84+ {
85+ let mut all_valid = true ;
86+
87+ for dr in doc_refs. iter ( ) {
88+ if let Some ( ref ref_doc) = provider. try_get_doc ( dr) . await ? {
89+ let is_valid = referenced_doc_type_check ( ref_doc, exp_ref_types, field_name, report)
90+ && referenced_doc_id_and_ver_check ( ref_doc, dr, field_name, report)
91+ && validator ( ref_doc) ;
92+
93+ if !is_valid {
94+ all_valid = false ;
95+ }
96+ } else {
97+ report. functional_validation (
98+ & format ! ( "Cannot retrieve a document {dr}" ) ,
99+ & format ! ( "Referenced document validation for the `{field_name}` field" ) ,
100+ ) ;
101+ all_valid = false ;
102+ }
103+ }
104+ Ok ( all_valid)
105+ }
106+
107+ /// Validation check that the provided `ref_doc` is a correct referenced document found by
108+ /// `original_doc_ref`
109+ fn referenced_doc_id_and_ver_check (
110+ ref_doc : & CatalystSignedDocument ,
111+ original_doc_ref : & DocumentRef ,
112+ field_name : & str ,
113+ report : & ProblemReport ,
114+ ) -> bool {
115+ let Ok ( id) = ref_doc. doc_id ( ) else {
116+ report. missing_field (
117+ "id" ,
118+ & format ! ( "Referenced document validation for the `{field_name}` field" ) ,
119+ ) ;
120+ return false ;
121+ } ;
122+
123+ let Ok ( ver) = ref_doc. doc_ver ( ) else {
124+ report. missing_field (
125+ "ver" ,
126+ & format ! ( "Referenced document validation for the `{field_name}` field" ) ,
127+ ) ;
128+ return false ;
129+ } ;
130+
131+ // id and version must match the values in ref doc
132+ if & id != original_doc_ref. id ( ) && & ver != original_doc_ref. ver ( ) {
133+ report. invalid_value (
134+ "id and version" ,
135+ & format ! ( "id: {id}, ver: {ver}" ) ,
136+ & format ! (
137+ "id: {}, ver: {}" ,
138+ original_doc_ref. id( ) ,
139+ original_doc_ref. ver( )
140+ ) ,
141+ & format ! ( "Referenced document validation for the `{field_name}` field" ) ,
142+ ) ;
143+ return false ;
144+ }
145+
146+ true
147+ }
148+
149+ /// Validation check that the provided `ref_doc` has an expected `type` field value from
150+ /// the allowed `exp_ref_types` list
151+ fn referenced_doc_type_check (
67152 ref_doc : & CatalystSignedDocument ,
68153 exp_ref_types : & [ DocType ] ,
69154 field_name : & str ,
70155 report : & ProblemReport ,
71156) -> bool {
72157 let Ok ( ref_doc_type) = ref_doc. doc_type ( ) else {
73- report. missing_field ( "type" , "Referenced document must have type field" ) ;
158+ report. missing_field (
159+ "type" ,
160+ & format ! ( "Document reference validation for the `{field_name}` field" ) ,
161+ ) ;
74162 return false ;
75163 } ;
76164
0 commit comments