use crate::ValidatorErrors;
use prefixmap::PrefixMap;
use prefixmap::error::PrefixMapError;
use serde::{Serialize, ser::SerializeMap};
use shex_ast::{
Node, ShapeLabelIdx,
ir::{node_constraint::NodeConstraint, schema_ir::SchemaIR, shape::Shape, shape_expr::ShapeExpr},
};
use std::{fmt::Display, io};
use termtree::Tree;
#[derive(Debug, Clone)]
pub enum Reason {
DescendantShape {
node: Node,
shape: ShapeLabelIdx,
reasons: Reasons,
},
ShapeExtends {
node: Node,
shape: Box<Shape>,
reasons: Reasons,
},
NodeConstraint {
node: Node,
nc: NodeConstraint,
},
ShapeAnd {
node: Node,
se: Box<ShapeExpr>,
reasons: Vec<Vec<Reason>>,
},
Empty {
node: Node,
},
External {
node: Node,
},
ShapeOr {
node: Node,
shape_expr: ShapeLabelIdx,
reasons: Reasons,
},
ShapeNot {
node: Node,
shape_expr: ShapeExpr,
errors_evidences: ValidatorErrors,
},
Shape {
node: Node,
shape: Box<Shape>,
idx: ShapeLabelIdx,
},
ShapeRef {
node: Node,
idx: ShapeLabelIdx,
},
}
impl Reason {
fn build_tree(
&self,
tree: &mut Tree<String>,
_nodes_prefixmap: &PrefixMap,
_schema: &SchemaIR,
) -> Result<(), PrefixMapError> {
match self {
Reason::NodeConstraint { .. } => Ok(()),
Reason::ShapeAnd { reasons, .. } => {
let mut reasons_tree = Tree::new("reasons".to_string());
for reason in reasons {
for r in reason {
r.build_tree(&mut reasons_tree, _nodes_prefixmap, _schema)?;
}
}
tree.leaves.push(reasons_tree);
Ok(())
},
Reason::ShapeOr { reasons, .. } => {
let mut reasons_tree = Tree::new("reasons".to_string());
for reason in reasons.iter() {
reason.build_tree(&mut reasons_tree, _nodes_prefixmap, _schema)?;
}
tree.leaves.push(reasons_tree);
Ok(())
},
_ => Ok(()),
}
}
pub fn root_qualified(
&self,
nodes_prefixmap: &PrefixMap,
schema: &SchemaIR,
width: usize,
) -> Result<String, PrefixMapError> {
match self {
Reason::NodeConstraint { node, nc } => Ok(format!(
"Node constraint passed. Node: {}, Constraint: {nc}",
node.show_qualified(nodes_prefixmap),
)),
Reason::ShapeAnd { node, se, .. } => {
let s = format!(
"AND passed. Node {}, and: {}",
node.show_qualified(nodes_prefixmap),
schema.show_shape_expr(se, width)
);
Ok(s)
},
Reason::Shape { node, idx, .. } => {
let se_str = schema.show_shape_idx(idx, width);
Ok(format!(
"Shape passed. Node {}, shape {}: {}",
node.show_qualified(nodes_prefixmap),
idx,
se_str
))
},
_ => Ok(format!("{self}",)),
}
}
pub fn write_qualified<W: io::Write>(
&self,
nodes_prefixmap: &PrefixMap,
schema: &SchemaIR,
width: usize,
writer: &mut W,
) -> Result<(), PrefixMapError> {
let root_str = self.root_qualified(nodes_prefixmap, schema, width)?;
let mut tree = Tree::new(root_str);
self.build_tree(&mut tree, nodes_prefixmap, schema)?;
write!(writer, "{}", tree).map_err(|e| PrefixMapError::IOError { error: e.to_string() })?;
Ok(())
}
pub fn show_qualified(
&self,
nodes_prefixmap: &PrefixMap,
schema: &SchemaIR,
width: usize,
) -> Result<String, PrefixMapError> {
let mut v = Vec::new();
self.write_qualified(nodes_prefixmap, schema, width, &mut v)?;
let s = String::from_utf8(v).map_err(|e| PrefixMapError::IOError { error: e.to_string() })?;
Ok(s)
}
}
impl Display for Reason {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Reason::NodeConstraint { node, nc } => {
write!(f, "Node constraint passed. Node: {node}, Constraint: {nc}",)
},
Reason::ShapeAnd { node, se, reasons } => {
write!(f, "AND passed. Node {node}, and: {se}, reasons:")?;
for reason in reasons {
write!(f, "[")?;
for r in reason {
write!(f, "{r}, ")?;
}
write!(f, "], ")?;
}
Ok(())
},
Reason::Shape { node, shape, idx } => {
write!(f, "Shape passed. Node {node}, shape {idx}: {shape}")
},
Reason::ShapeOr {
node,
shape_expr,
reasons,
} => write!(
f,
"Shape OR passed. Node {node}, shape: {shape_expr}, reasons: {reasons}"
),
Reason::ShapeNot {
node,
shape_expr,
errors_evidences,
} => write!(
f,
"Shape NOT passed. Node {node}, shape: {shape_expr}, errors: {errors_evidences}"
),
Reason::External { node } => write!(f, "Shape External passed for node {node}"),
Reason::Empty { node } => write!(f, "Shape External passed for node {node}"),
Reason::ShapeRef { node, idx } => {
write!(f, "ShapeRef passed. Node {node}, idx: {idx}")
},
Reason::ShapeExtends { node, shape, reasons } => write!(
f,
"Shape extends passed. Node {node}, shape: {shape}, reasons: {reasons}"
),
Reason::DescendantShape { node, shape, reasons } => write!(
f,
"Descendant shapes passed. Node {node}, shape: {shape}, reasons: {reasons}"
),
}
}
}
impl Reason {
pub fn as_json(&self) -> Result<serde_json::Value, serde_json::Error> {
serde_json::to_value(self)
}
}
#[derive(Debug, Clone)]
pub struct Reasons {
reasons: Vec<Reason>,
}
impl Reasons {
pub fn new(reasons: Vec<Reason>) -> Reasons {
Reasons { reasons }
}
pub fn iter(&self) -> std::slice::Iter<'_, Reason> {
self.reasons.iter()
}
}
impl Display for Reasons {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
for reason in self.reasons.iter() {
writeln!(f, " {reason}")?;
}
Ok(())
}
}
impl Serialize for Reason {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
let mut map = serializer.serialize_map(Some(1))?;
map.serialize_entry("reason", &self.to_string())?;
map.end()
}
}