use std::cell::RefCell;
use eure_document::document::EureDocument;
use eure_document::identifier::Identifier;
use eure_document::path::{ArrayIndexKind, EurePath, PathSegment};
use eure_document::value::ObjectKey;
use crate::{SchemaDocument, SchemaNodeContent, SchemaNodeId};
use super::error::{ValidationError, ValidationWarning};
#[derive(Debug, Clone, Default)]
pub struct ValidationOutput {
pub is_valid: bool,
pub is_complete: bool,
pub errors: Vec<ValidationError>,
pub warnings: Vec<ValidationWarning>,
}
#[derive(Debug)]
pub struct ValidationState {
pub path: EurePath,
pub has_holes: bool,
pub errors: Vec<ValidationError>,
pub warnings: Vec<ValidationWarning>,
pub(crate) variant_errors: Vec<(String, SchemaNodeId, Vec<ValidationError>)>,
}
impl Default for ValidationState {
fn default() -> Self {
Self {
path: EurePath::root(),
has_holes: false,
errors: Vec::new(),
warnings: Vec::new(),
variant_errors: Vec::new(),
}
}
}
impl ValidationState {
pub fn new() -> Self {
Self::default()
}
pub fn record_error(&mut self, error: ValidationError) {
self.errors.push(error);
}
pub fn record_warning(&mut self, warning: ValidationWarning) {
self.warnings.push(warning);
}
pub fn mark_has_holes(&mut self) {
self.has_holes = true;
}
pub fn has_errors(&self) -> bool {
!self.errors.is_empty()
}
pub fn error_count(&self) -> usize {
self.errors.len()
}
pub fn push_path_ident(&mut self, ident: Identifier) {
self.path.0.push(PathSegment::Ident(ident));
}
pub fn push_path_key(&mut self, key: ObjectKey) {
self.path.0.push(PathSegment::Value(key));
}
pub fn push_path_index(&mut self, index: usize) {
self.path
.0
.push(PathSegment::ArrayIndex(ArrayIndexKind::Specific(index)));
}
pub fn push_path_tuple_index(&mut self, index: u8) {
self.path.0.push(PathSegment::TupleIndex(index));
}
pub fn push_path_extension(&mut self, ident: Identifier) {
self.path.0.push(PathSegment::Extension(ident));
}
pub fn pop_path(&mut self) {
self.path.0.pop();
}
pub fn fork(&self) -> Self {
Self {
path: self.path.clone(),
has_holes: self.has_holes,
errors: Vec::new(),
warnings: Vec::new(),
variant_errors: Vec::new(), }
}
pub fn merge(&mut self, other: Self) {
self.has_holes |= other.has_holes;
self.errors.extend(other.errors);
self.warnings.extend(other.warnings);
}
pub fn finish(self) -> ValidationOutput {
ValidationOutput {
is_valid: self.errors.is_empty(),
is_complete: self.errors.is_empty() && !self.has_holes,
errors: self.errors,
warnings: self.warnings,
}
}
pub fn record_variant_errors(
&mut self,
variant_name: String,
schema_id: SchemaNodeId,
errors: Vec<ValidationError>,
) {
self.variant_errors.push((variant_name, schema_id, errors));
}
pub fn take_variant_errors(&mut self) -> Vec<(String, SchemaNodeId, Vec<ValidationError>)> {
std::mem::take(&mut self.variant_errors)
}
pub fn clear_variant_errors(&mut self) {
self.variant_errors.clear();
}
}
pub struct ValidationContext<'a> {
pub schema: &'a SchemaDocument,
pub document: &'a EureDocument,
pub state: RefCell<ValidationState>,
}
impl<'a> ValidationContext<'a> {
pub fn new(document: &'a EureDocument, schema: &'a SchemaDocument) -> Self {
Self {
schema,
document,
state: RefCell::new(ValidationState::new()),
}
}
pub fn with_state(
document: &'a EureDocument,
schema: &'a SchemaDocument,
state: ValidationState,
) -> Self {
Self {
schema,
document,
state: RefCell::new(state),
}
}
pub fn record_error(&self, error: ValidationError) {
self.state.borrow_mut().record_error(error);
}
pub fn record_warning(&self, warning: ValidationWarning) {
self.state.borrow_mut().record_warning(warning);
}
pub fn mark_has_holes(&self) {
self.state.borrow_mut().mark_has_holes();
}
pub fn has_errors(&self) -> bool {
self.state.borrow().has_errors()
}
pub fn error_count(&self) -> usize {
self.state.borrow().error_count()
}
pub fn path(&self) -> EurePath {
self.state.borrow().path.clone()
}
pub fn push_path_ident(&self, ident: Identifier) {
self.state.borrow_mut().push_path_ident(ident);
}
pub fn push_path_key(&self, key: ObjectKey) {
self.state.borrow_mut().push_path_key(key);
}
pub fn push_path_index(&self, index: usize) {
self.state.borrow_mut().push_path_index(index);
}
pub fn push_path_tuple_index(&self, index: u8) {
self.state.borrow_mut().push_path_tuple_index(index);
}
pub fn push_path_extension(&self, ident: Identifier) {
self.state.borrow_mut().push_path_extension(ident);
}
pub fn pop_path(&self) {
self.state.borrow_mut().pop_path();
}
pub fn fork_state(&self) -> ValidationState {
self.state.borrow().fork()
}
pub fn merge_state(&self, other: ValidationState) {
self.state.borrow_mut().merge(other);
}
pub fn resolve_schema_content(&self, schema_id: SchemaNodeId) -> &SchemaNodeContent {
let mut current_id = schema_id;
for _ in 0..100 {
let content = &self.schema.node(current_id).content;
match content {
SchemaNodeContent::Reference(type_ref) => {
if type_ref.namespace.is_some() {
return content; }
if let Some(&resolved_id) = self.schema.types.get(&type_ref.name) {
current_id = resolved_id;
} else {
return content; }
}
_ => return content,
}
}
&self.schema.node(current_id).content
}
pub fn parse_context(
&self,
node_id: eure_document::document::NodeId,
) -> eure_document::parse::ParseContext<'a> {
eure_document::parse::ParseContext::new(self.document, node_id)
}
pub fn record_variant_errors(
&self,
variant_name: String,
schema_id: SchemaNodeId,
errors: Vec<ValidationError>,
) {
self.state
.borrow_mut()
.record_variant_errors(variant_name, schema_id, errors);
}
pub fn take_variant_errors(&self) -> Vec<(String, SchemaNodeId, Vec<ValidationError>)> {
self.state.borrow_mut().take_variant_errors()
}
pub fn clear_variant_errors(&self) {
self.state.borrow_mut().clear_variant_errors();
}
pub fn finish(self) -> ValidationOutput {
self.state.into_inner().finish()
}
}