fog_pack/validator/
checklist.rs1use std::collections::HashMap;
2
3use super::*;
4use crate::Hash;
5use crate::{
6 document::Document,
7 error::{Error, Result},
8};
9
10#[derive(Clone, Debug)]
15pub struct ListItem<'a> {
16 inner: InnerListItem<'a>,
17 schema: &'a Hash,
18 types: &'a BTreeMap<String, Validator>,
19}
20
21impl<'a> ListItem<'a> {
22 pub fn check(self, doc: &Document) -> Result<()> {
26 if !self.inner.schema.is_empty() {
28 let doc_schema = match doc.schema_hash() {
29 Some(schema) => schema,
30 None => {
31 return Err(Error::FailValidate(
32 "Document has no schema, but must pass `schema` validation".into(),
33 ))
34 }
35 };
36 let all_schema_pass = self.inner.schema.iter().all(|list| {
37 list.iter().any(|schema| match schema {
38 None => self.schema == doc_schema,
39 Some(schema) => schema == doc_schema,
40 })
41 });
42 if !all_schema_pass {
43 return Err(Error::FailValidate(
44 "Document schema didn't satisfy all `schema` requirements".into(),
45 ));
46 }
47 }
48
49 let parser = Parser::new(doc.data());
55 let all_link_pass = self
56 .inner
57 .link
58 .iter()
59 .all(|validator| validator.validate(self.types, parser.clone(), None).is_ok());
60 if !all_link_pass {
61 return Err(Error::FailValidate(
62 "Document schema didn't satisfy all `link` requirements".into(),
63 ));
64 }
65 Ok(())
66 }
67}
68
69#[derive(Clone, Debug)]
70struct InnerListItem<'a> {
71 schema: Vec<&'a [Option<Hash>]>,
72 link: Vec<&'a Validator>,
73}
74
75impl<'a> InnerListItem<'a> {
76 fn new() -> Self {
77 Self {
78 schema: Vec::new(),
79 link: Vec::new(),
80 }
81 }
82}
83
84#[derive(Clone, Debug)]
96pub struct DataChecklist<'a, T> {
97 list: Checklist<'a>,
98 data: T,
99}
100
101impl<'a, T> DataChecklist<'a, T> {
102 pub(crate) fn from_checklist(list: Checklist<'a>, data: T) -> Self {
103 Self { list, data }
104 }
105
106 pub fn iter(&mut self) -> impl Iterator<Item = (Hash, ListItem)> {
110 self.list.iter()
111 }
112
113 pub fn complete(self) -> Result<T> {
116 self.list.complete()?;
117 Ok(self.data)
118 }
119}
120
121#[derive(Clone, Debug)]
122pub(crate) struct Checklist<'a> {
123 list: HashMap<Hash, InnerListItem<'a>>,
124 types: &'a BTreeMap<String, Validator>,
125 schema: &'a Hash,
126}
127
128impl<'a> Checklist<'a> {
129 pub(crate) fn new(schema: &'a Hash, types: &'a BTreeMap<String, Validator>) -> Self {
130 Self {
131 list: HashMap::new(),
132 types,
133 schema,
134 }
135 }
136
137 pub(crate) fn insert(
138 &mut self,
139 hash: Hash,
140 schema: Option<&'a [Option<Hash>]>,
141 link: Option<&'a Validator>,
142 ) {
143 let entry = self.list.entry(hash).or_insert_with(InnerListItem::new);
144 if let Some(schema) = schema {
145 entry.schema.push(schema)
146 }
147 if let Some(link) = link {
148 entry.link.push(link)
149 }
150 }
151
152 pub(crate) fn iter(&mut self) -> impl Iterator<Item = (Hash, ListItem)> {
155 let schema = self.schema;
156 let types = self.types;
157 self.list.drain().map(move |(doc, inner)| {
158 (
159 doc,
160 ListItem {
161 inner,
162 types,
163 schema,
164 },
165 )
166 })
167 }
168
169 fn complete(self) -> Result<()> {
171 if self.list.is_empty() {
172 Ok(())
173 } else {
174 Err(Error::FailValidate(
175 "Not all verification checklist items were completed".into(),
176 ))
177 }
178 }
179}
180
181#[cfg(test)]
182mod test {
183 use crate::{document::NewDocument, schema::*, types::Integer};
184
185 use super::*;
186
187 #[test]
188 fn runthrough() {
189 let schema1 = SchemaBuilder::new(IntValidator::default().build())
191 .build()
192 .unwrap();
193 let schema1 = Schema::from_doc(&schema1).unwrap();
194 let validator = IntValidator {
195 min: Integer::from(0),
196 ..IntValidator::default()
197 }
198 .build();
199 let schema2 = SchemaBuilder::new(validator).build().unwrap();
200 let schema2 = Schema::from_doc(&schema2).unwrap();
201
202 let doc1 = NoSchema::validate_new_doc(NewDocument::new(None, 0u8).unwrap()).unwrap();
203 let doc2 = NoSchema::validate_new_doc(NewDocument::new(None, 1u8).unwrap()).unwrap();
204 let doc3 = schema1
205 .validate_new_doc(NewDocument::new(Some(schema1.hash()), 0u8).unwrap())
206 .unwrap();
207 let doc4 = schema2
208 .validate_new_doc(NewDocument::new(Some(schema2.hash()), 0u8).unwrap())
209 .unwrap();
210
211 let types = BTreeMap::new();
212 let mut checklist = Checklist::new(schema1.hash(), &types);
213 let validator = IntValidator {
214 min: Integer::from(0u32),
215 ..IntValidator::default()
216 }
217 .build();
218 let schema2_schema = [Some(schema2.hash().clone())];
219 checklist.insert(doc1.hash().clone(), None, Some(&validator));
220 checklist.insert(doc2.hash().clone(), None, Some(&validator));
221 checklist.insert(doc3.hash().clone(), Some(&[None]), None);
222 checklist.insert(doc4.hash().clone(), Some(&schema2_schema), Some(&validator));
223 let mut checklist = DataChecklist::from_checklist(checklist, ());
224
225 let mut map = HashMap::new();
226 map.insert(doc1.hash().clone(), doc1);
227 map.insert(doc2.hash().clone(), doc2);
228 map.insert(doc3.hash().clone(), doc3);
229 map.insert(doc4.hash().clone(), doc4);
230
231 checklist
232 .iter()
233 .try_for_each(|(hash, item)| {
234 let doc = map
235 .get(&hash)
236 .ok_or_else(|| Error::FailValidate("".into()))?;
237 item.check(doc)
238 })
239 .unwrap();
240 checklist.complete().unwrap();
241 }
242}