1use std::cell::RefCell;
10
11use eure_document::document::EureDocument;
12use eure_document::identifier::Identifier;
13use eure_document::parse::UnionTagMode;
14use eure_document::path::{EurePath, PathSegment};
15use eure_document::value::ObjectKey;
16
17use crate::{SchemaDocument, SchemaNodeContent, SchemaNodeId};
18
19use super::error::{ValidationError, ValidationWarning};
20
21#[derive(Debug, Clone, Default)]
27pub struct ValidationOutput {
28 pub is_valid: bool,
30 pub is_complete: bool,
32 pub errors: Vec<ValidationError>,
34 pub warnings: Vec<ValidationWarning>,
36}
37
38#[derive(Debug)]
46pub struct ValidationState {
47 pub path: EurePath,
49 pub has_holes: bool,
51 pub errors: Vec<ValidationError>,
53 pub warnings: Vec<ValidationWarning>,
55 pub(crate) variant_errors: Vec<(String, SchemaNodeId, Vec<ValidationError>)>,
58}
59
60impl Default for ValidationState {
61 fn default() -> Self {
62 Self {
63 path: EurePath::root(),
64 has_holes: false,
65 errors: Vec::new(),
66 warnings: Vec::new(),
67 variant_errors: Vec::new(),
68 }
69 }
70}
71
72impl ValidationState {
73 pub fn new() -> Self {
74 Self::default()
75 }
76
77 pub fn record_error(&mut self, error: ValidationError) {
79 self.errors.push(error);
80 }
81
82 pub fn record_warning(&mut self, warning: ValidationWarning) {
84 self.warnings.push(warning);
85 }
86
87 pub fn mark_has_holes(&mut self) {
89 self.has_holes = true;
90 }
91
92 pub fn has_errors(&self) -> bool {
94 !self.errors.is_empty()
95 }
96
97 pub fn error_count(&self) -> usize {
99 self.errors.len()
100 }
101
102 pub fn push_path_ident(&mut self, ident: Identifier) {
108 self.path.0.push(PathSegment::Ident(ident));
109 }
110
111 pub fn push_path_key(&mut self, key: ObjectKey) {
113 self.path.0.push(PathSegment::Value(key));
114 }
115
116 pub fn push_path_index(&mut self, index: usize) {
118 self.path.0.push(PathSegment::ArrayIndex(Some(index)));
119 }
120
121 pub fn push_path_tuple_index(&mut self, index: u8) {
123 self.path.0.push(PathSegment::TupleIndex(index));
124 }
125
126 pub fn push_path_extension(&mut self, ident: Identifier) {
128 self.path.0.push(PathSegment::Extension(ident));
129 }
130
131 pub fn pop_path(&mut self) {
133 self.path.0.pop();
134 }
135
136 pub fn fork(&self) -> Self {
138 Self {
139 path: self.path.clone(),
140 has_holes: self.has_holes,
141 errors: Vec::new(),
142 warnings: Vec::new(),
143 variant_errors: Vec::new(), }
145 }
146
147 pub fn merge(&mut self, other: Self) {
149 self.has_holes |= other.has_holes;
150 self.errors.extend(other.errors);
151 self.warnings.extend(other.warnings);
152 }
153
154 pub fn finish(self) -> ValidationOutput {
156 ValidationOutput {
157 is_valid: self.errors.is_empty(),
158 is_complete: self.errors.is_empty() && !self.has_holes,
159 errors: self.errors,
160 warnings: self.warnings,
161 }
162 }
163
164 pub fn record_variant_errors(
170 &mut self,
171 variant_name: String,
172 schema_id: SchemaNodeId,
173 errors: Vec<ValidationError>,
174 ) {
175 self.variant_errors.push((variant_name, schema_id, errors));
176 }
177
178 pub fn take_variant_errors(&mut self) -> Vec<(String, SchemaNodeId, Vec<ValidationError>)> {
180 std::mem::take(&mut self.variant_errors)
181 }
182
183 pub fn clear_variant_errors(&mut self) {
185 self.variant_errors.clear();
186 }
187}
188
189pub struct ValidationContext<'a> {
198 pub schema: &'a SchemaDocument,
200 pub document: &'a EureDocument,
202 pub state: RefCell<ValidationState>,
204 pub union_tag_mode: UnionTagMode,
206}
207
208impl<'a> ValidationContext<'a> {
209 pub fn new(document: &'a EureDocument, schema: &'a SchemaDocument) -> Self {
211 Self::with_mode(document, schema, UnionTagMode::default())
212 }
213
214 pub fn with_mode(
216 document: &'a EureDocument,
217 schema: &'a SchemaDocument,
218 union_tag_mode: UnionTagMode,
219 ) -> Self {
220 Self {
221 schema,
222 document,
223 state: RefCell::new(ValidationState::new()),
224 union_tag_mode,
225 }
226 }
227
228 pub fn with_state(
230 document: &'a EureDocument,
231 schema: &'a SchemaDocument,
232 state: ValidationState,
233 ) -> Self {
234 Self::with_state_and_mode(document, schema, state, UnionTagMode::default())
235 }
236
237 pub fn with_state_and_mode(
239 document: &'a EureDocument,
240 schema: &'a SchemaDocument,
241 state: ValidationState,
242 union_tag_mode: UnionTagMode,
243 ) -> Self {
244 Self {
245 schema,
246 document,
247 state: RefCell::new(state),
248 union_tag_mode,
249 }
250 }
251
252 pub fn record_error(&self, error: ValidationError) {
254 self.state.borrow_mut().record_error(error);
255 }
256
257 pub fn record_warning(&self, warning: ValidationWarning) {
259 self.state.borrow_mut().record_warning(warning);
260 }
261
262 pub fn mark_has_holes(&self) {
264 self.state.borrow_mut().mark_has_holes();
265 }
266
267 pub fn has_errors(&self) -> bool {
269 self.state.borrow().has_errors()
270 }
271
272 pub fn error_count(&self) -> usize {
274 self.state.borrow().error_count()
275 }
276
277 pub fn path(&self) -> EurePath {
279 self.state.borrow().path.clone()
280 }
281
282 pub fn push_path_ident(&self, ident: Identifier) {
284 self.state.borrow_mut().push_path_ident(ident);
285 }
286
287 pub fn push_path_key(&self, key: ObjectKey) {
289 self.state.borrow_mut().push_path_key(key);
290 }
291
292 pub fn push_path_index(&self, index: usize) {
294 self.state.borrow_mut().push_path_index(index);
295 }
296
297 pub fn push_path_tuple_index(&self, index: u8) {
299 self.state.borrow_mut().push_path_tuple_index(index);
300 }
301
302 pub fn push_path_extension(&self, ident: Identifier) {
304 self.state.borrow_mut().push_path_extension(ident);
305 }
306
307 pub fn pop_path(&self) {
309 self.state.borrow_mut().pop_path();
310 }
311
312 pub fn fork_state(&self) -> ValidationState {
314 self.state.borrow().fork()
315 }
316
317 pub fn merge_state(&self, other: ValidationState) {
319 self.state.borrow_mut().merge(other);
320 }
321
322 pub fn resolve_schema_content(&self, schema_id: SchemaNodeId) -> &SchemaNodeContent {
324 let mut current_id = schema_id;
325 for _ in 0..100 {
327 let content = &self.schema.node(current_id).content;
328 match content {
329 SchemaNodeContent::Reference(type_ref) => {
330 if type_ref.namespace.is_some() {
331 return content; }
333 if let Some(&resolved_id) = self.schema.types.get(&type_ref.name) {
334 current_id = resolved_id;
335 } else {
336 return content; }
338 }
339 _ => return content,
340 }
341 }
342 &self.schema.node(current_id).content
343 }
344
345 pub fn parse_context(
347 &self,
348 node_id: eure_document::document::NodeId,
349 ) -> eure_document::parse::ParseContext<'a> {
350 eure_document::parse::ParseContext::with_union_tag_mode(
351 self.document,
352 node_id,
353 self.union_tag_mode,
354 )
355 }
356
357 pub fn record_variant_errors(
363 &self,
364 variant_name: String,
365 schema_id: SchemaNodeId,
366 errors: Vec<ValidationError>,
367 ) {
368 self.state
369 .borrow_mut()
370 .record_variant_errors(variant_name, schema_id, errors);
371 }
372
373 pub fn take_variant_errors(&self) -> Vec<(String, SchemaNodeId, Vec<ValidationError>)> {
375 self.state.borrow_mut().take_variant_errors()
376 }
377
378 pub fn clear_variant_errors(&self) {
380 self.state.borrow_mut().clear_variant_errors();
381 }
382
383 pub fn finish(self) -> ValidationOutput {
385 self.state.into_inner().finish()
386 }
387}