use super::node_constraint::NodeConstraint;
use crate::ir::annotation::Annotation;
use crate::ir::map_action_extension::MapActionExtension;
use crate::ir::map_state::MapState;
use crate::ir::object_value::ObjectValue;
use crate::ir::schema_ir::SchemaIR;
use crate::ir::sem_act::SemAct;
use crate::ir::semantic_action_context::SemanticActionContext;
use crate::ir::semantic_action_extension::SemanticActionExtension;
use crate::ir::semantic_actions_registry::SemanticActionsRegistry;
use crate::ir::shape::Shape;
use crate::ir::shape_expr::ShapeExpr;
use crate::ir::shape_label::ShapeLabel;
use crate::ir::test_action_extension::TestActionExtension;
use crate::ir::value_set::ValueSet;
use crate::ir::value_set_value::ValueSetValue;
use crate::{CResult, Cond, Node, Pred, ResolveMethod, ShExFormat, ShExParser, ir};
use crate::{SchemaIRError, ShapeLabelIdx, ast, ast::Schema as SchemaAST};
use crate::{ShapeExprLabel, ast::iri_exclusion::IriExclusion};
use core::panic;
use prefixmap::{IriRef, PrefixMap};
use rbe::{Cardinality, Pending, RbeError, SingleCond};
use rbe::{Component, MatchCond, Max, Min, RbeTable, rbe::Rbe};
use rudof_iri::IriS;
use rudof_iri::error::IriSError;
use rudof_rdf::rdf_core::term::{
Object,
literal::{ConcreteLiteral, NumericLiteral},
};
use std::collections::HashMap;
use std::sync::Arc;
use tracing::{debug, info, trace};
#[derive(Debug, Default)]
pub struct AST2IR {
shape_decls_counter: usize,
resolve_method: ResolveMethod,
semantic_actions_registry: SemanticActionsRegistry,
triple_expr_labels: HashMap<ast::TripleExprLabel, ast::TripleExpr>,
}
impl AST2IR {
pub fn new(resolve_method: &ResolveMethod, map_state: MapState) -> Self {
let semantic_actions_registry = SemanticActionsRegistry::new().with(vec![
Box::new(TestActionExtension::new()),
Box::new(MapActionExtension::new(map_state)),
]);
Self {
resolve_method: resolve_method.clone(),
shape_decls_counter: 0,
semantic_actions_registry,
triple_expr_labels: HashMap::new(),
}
}
pub fn with_registry(resolve_method: &ResolveMethod, registry: SemanticActionsRegistry) -> Self {
Self {
resolve_method: resolve_method.clone(),
shape_decls_counter: 0,
semantic_actions_registry: registry,
triple_expr_labels: HashMap::new(),
}
}
pub fn compile(
&mut self,
schema_ast: &SchemaAST,
source_iri: &IriS,
base: &Option<IriS>,
compiled_schema: &mut SchemaIR,
) -> CResult<()> {
let mut visited = Vec::new();
let mut imported_asts: Vec<(SchemaAST, IriS)> = Vec::new();
trace!(
"Compiling schema from {source_iri}. Base: {}",
base.as_ref().map(|b| b.as_str()).unwrap_or("None")
);
self.collect_imports_labels(
schema_ast,
source_iri,
compiled_schema,
&mut visited,
base,
&mut imported_asts,
)?;
compiled_schema.set_prefixmap(schema_ast.prefixmap());
let local_shapes = self.collect_shape_labels(schema_ast, compiled_schema, source_iri)?;
self.triple_expr_labels.clear();
for (ast, _) in imported_asts.iter() {
self.collect_triple_expr_labels_schema(ast)?;
}
self.collect_triple_expr_labels_schema(schema_ast)?;
for (ast, src) in imported_asts.iter() {
self.collect_shape_exprs(ast, compiled_schema, src)?;
}
self.collect_shape_exprs(schema_ast, compiled_schema, source_iri)?;
compiled_schema.set_local_shapes_counter(local_shapes);
compiled_schema.set_imported_schemas(visited);
compiled_schema.increment_total_shapes(local_shapes);
compiled_schema.build_dependency_graph();
compiled_schema.build_inheritance_graph();
Ok(())
}
fn collect_imports_labels(
&mut self,
schema_ast: &SchemaAST,
source_iri: &IriS,
compiled_schema: &mut SchemaIR,
visited_sources: &mut Vec<IriS>,
base: &Option<IriS>,
imported_asts: &mut Vec<(SchemaAST, IriS)>,
) -> CResult<()> {
let imports = schema_ast
.imports_resolved(base)
.map_err(|e| Box::new(SchemaIRError::ImportIriError { error: e.to_string() }))?;
for import_iri in imports {
trace!("Import IRI resolved to {import_iri}");
if !visited_sources.contains(&import_iri) {
visited_sources.push(import_iri.clone());
let imported_schema = self.resolve(&import_iri, Some(source_iri))?;
if imported_schema.start_actions().is_some() {
return Err(Box::new(SchemaIRError::ImportIriError {
error: format!("Imported schema {import_iri} declares startActs (disallowed by ShEx spec)"),
}));
}
let nested_base = Some(import_iri.clone());
self.collect_imports_labels(
&imported_schema,
&import_iri,
compiled_schema,
visited_sources,
&nested_base,
imported_asts,
)?;
self.collect_imported_shape_labels(&imported_schema, compiled_schema, &import_iri)?;
imported_asts.push((imported_schema, import_iri));
}
}
Ok(())
}
fn collect_imported_shape_labels(
&mut self,
schema_ast: &SchemaAST,
compiled_schema: &mut SchemaIR,
source_iri: &IriS,
) -> CResult<usize> {
let mut shape_labels_counter = 0;
if let Some(shape_decls) = &schema_ast.shapes() {
for shape_decl in shape_decls {
let label = self.shape_expr_label_to_shape_label(&shape_decl.id)?;
let idx = compiled_schema.add_shape(label, ShapeExpr::Empty, source_iri);
if shape_decl.is_abstract {
compiled_schema.add_abstract_shape(idx);
}
self.shape_decls_counter += 1;
shape_labels_counter += 1;
}
}
Ok(shape_labels_counter)
}
fn resolve(&self, iri: &IriS, base: Option<&IriS>) -> CResult<SchemaAST> {
let new_schema = match &self.resolve_method {
ResolveMethod::RotatingFormats(formats) => find_schema_rotating_formats(iri, formats.clone(), base),
ResolveMethod::ByGuessingExtension => todo!(),
ResolveMethod::ByContentNegotiation => todo!(),
}?;
Ok(new_schema)
}
pub fn collect_shape_labels(
&mut self,
schema_ast: &SchemaAST,
compiled_schema: &mut SchemaIR,
source_iri: &IriS,
) -> CResult<usize> {
let mut shape_labels_counter = 0;
match &schema_ast.shapes() {
None => {},
Some(shape_decls) => {
for shape_decl in shape_decls {
let label = self.shape_expr_label_to_shape_label(&shape_decl.id)?;
let idx = compiled_schema.add_shape(label, ShapeExpr::Empty, source_iri);
if shape_decl.is_abstract {
compiled_schema.add_abstract_shape(idx);
}
self.shape_decls_counter += 1;
shape_labels_counter += 1;
}
},
}
if let Some(shape_expr_start) = &schema_ast.start() {
let start_label = ShapeLabel::Start;
let idx = compiled_schema.add_shape(start_label.clone(), ShapeExpr::Empty, source_iri);
let start_compiled = self.compile_shape_expr(shape_expr_start, &idx, compiled_schema, source_iri)?;
compiled_schema.replace_shape(&idx, start_compiled);
self.shape_decls_counter += 1;
shape_labels_counter += 1;
}
Ok(shape_labels_counter)
}
pub fn collect_shape_exprs(
&mut self,
schema_ast: &SchemaAST,
compiled_schema: &mut SchemaIR,
source_iri: &IriS,
) -> CResult<()> {
match &schema_ast.shapes() {
None => Ok(()),
Some(sds) => {
for sd in sds {
let idx = self.get_shape_label_idx(&sd.id, compiled_schema)?;
let se = self.compile_shape_decl(sd, &idx, compiled_schema, source_iri)?;
compiled_schema.replace_shape(&idx, se)
}
Ok(())
},
}
}
fn shape_expr_label_to_shape_label(&self, id: &ShapeExprLabel) -> CResult<ShapeLabel> {
match id {
ShapeExprLabel::IriRef { value } => {
let shape_label = iri_ref_2_shape_label(value)?;
Ok(shape_label)
},
ShapeExprLabel::BNode { value } => Ok(ShapeLabel::BNode(value.clone())),
ShapeExprLabel::Start => Ok(ShapeLabel::Start),
}
}
fn get_shape_label_idx(&self, id: &ShapeExprLabel, compiled_schema: &mut SchemaIR) -> CResult<ShapeLabelIdx> {
let label = self.shape_expr_label_to_shape_label(id)?;
compiled_schema.get_shape_label_idx(&label)
}
fn compile_shape_decl(
&self,
sd: &ast::ShapeDecl,
idx: &ShapeLabelIdx,
compiled_schema: &mut SchemaIR,
source_iri: &IriS,
) -> CResult<ShapeExpr> {
let se = self.compile_shape_expr(&sd.shape_expr, idx, compiled_schema, source_iri)?;
Ok(se)
}
fn ref2idx(&self, sref: &ast::ShapeExprLabel, compiled_schema: &mut SchemaIR) -> CResult<ShapeLabelIdx> {
let idx = self.get_shape_label_idx(sref, compiled_schema)?;
Ok(idx)
}
fn compile_shape_expr(
&self,
se: &ast::ShapeExpr,
idx: &ShapeLabelIdx,
compiled_schema: &mut SchemaIR,
source_iri: &IriS,
) -> CResult<ShapeExpr> {
let result: ShapeExpr = match se {
ast::ShapeExpr::Ref(se_ref) => {
let new_idx = self.ref2idx(se_ref, compiled_schema)?;
let se: ShapeExpr = ShapeExpr::Ref { idx: new_idx };
Ok::<ShapeExpr, SchemaIRError>(se)
},
ast::ShapeExpr::ShapeOr { shape_exprs: ses } => {
tracing::debug!("Compiling ShapeOr with {ses:?}");
let mut cnv = Vec::new();
for sew in ses {
let internal_idx = compiled_schema.new_index(source_iri);
let se = self.compile_shape_expr(&sew.se, &internal_idx, compiled_schema, source_iri)?;
compiled_schema.replace_shape(&internal_idx, se.clone());
cnv.push(internal_idx);
}
let result = ShapeExpr::ShapeOr { exprs: cnv };
compiled_schema.replace_shape(idx, result.clone());
tracing::debug!("ShapeOr result: {result:?}");
Ok(result)
},
ast::ShapeExpr::ShapeAnd { shape_exprs: ses } => {
tracing::debug!("Compiling ShapeAnd with {ses:?}");
let mut cnv = Vec::new();
for sew in ses {
let internal_idx = compiled_schema.new_index(source_iri);
let se = self.compile_shape_expr(&sew.se, &internal_idx, compiled_schema, source_iri)?;
compiled_schema.replace_shape(&internal_idx, se.clone());
cnv.push(internal_idx);
}
let result = ShapeExpr::ShapeAnd { exprs: cnv };
compiled_schema.replace_shape(idx, result.clone());
tracing::debug!("ShapeAnd result: {result:?}");
Ok(result)
},
ast::ShapeExpr::ShapeNot { shape_expr: sew } => {
trace!("Compiling ShapeNot with {sew:?} and index {idx}");
let internal_idx = compiled_schema.new_index(source_iri);
let se = self.compile_shape_expr(&sew.se, &internal_idx, compiled_schema, source_iri)?;
compiled_schema.replace_shape(&internal_idx, se.clone());
let not_se = ShapeExpr::ShapeNot { expr: internal_idx };
compiled_schema.replace_shape(idx, not_se.clone());
Ok(not_se)
},
ast::ShapeExpr::Shape(shape) => {
let new_extra = self.cnv_extra(&shape.extra, &compiled_schema.prefixmap())?;
let rbe_table = match &shape.expression {
None => RbeTable::new(),
Some(tew) => {
let mut table = RbeTable::new();
let rbe = self.triple_expr2rbe(&tew.te, compiled_schema, &mut table, source_iri)?;
table.with_rbe(rbe);
table
},
};
let preds = self.get_preds_shape(shape)?;
let extends = shape
.extends()
.iter()
.map(|s| self.ref2idx(s, compiled_schema))
.collect::<CResult<Vec<_>>>()?;
let sem_acts = self.cnv_sem_actions(&shape.sem_acts, &compiled_schema.prefixmap())?;
let shape = Shape::new(
Self::cnv_closed(&shape.closed),
new_extra,
rbe_table,
sem_acts,
Self::cnv_annotations(&shape.annotations),
preds,
extends,
);
Ok(ShapeExpr::Shape(Box::new(shape)))
},
ast::ShapeExpr::NodeConstraint(nc) => {
let (cond, display) = Self::cnv_node_constraint(
self,
&nc.node_kind(),
&nc.datatype(),
&nc.xs_facet(),
&nc.values(),
&compiled_schema.prefixmap(),
)?;
let node_constraint = NodeConstraint::new(nc.clone(), cond, display);
Ok(ShapeExpr::NodeConstraint(node_constraint))
},
ast::ShapeExpr::External => Ok(ShapeExpr::External {}),
}?;
trace!("Result of compilation: {result}");
Ok(result)
}
fn cnv_node_constraint(
&self,
nk: &Option<ast::NodeKind>,
dt: &Option<IriRef>,
xs_facet: &Option<Vec<ast::XsFacet>>,
values: &Option<Vec<ast::ValueSetValue>>,
prefixmap: &PrefixMap,
) -> CResult<(Cond, String)> {
let maybe_value_set = match values {
Some(vs) => {
let value_set = create_value_set(vs, prefixmap)?;
Some(value_set)
},
None => None,
};
node_constraint2match_cond(nk, dt, xs_facet, &maybe_value_set, prefixmap)
}
fn cnv_closed(closed: &Option<bool>) -> bool {
match closed {
None => false,
Some(closed) => *closed,
}
}
fn cnv_extra(&self, extra: &Option<Vec<IriRef>>, prefixmap: &PrefixMap) -> CResult<Vec<Pred>> {
if let Some(extra) = extra {
let mut vs = Vec::new();
for iri in extra {
let nm = cnv_iri_ref(iri, prefixmap)?;
vs.push(Pred::new(nm));
}
Ok(vs)
} else {
Ok(Vec::new())
}
}
fn cnv_annotations(annotations: &Option<Vec<ast::Annotation>>) -> Vec<Annotation> {
if let Some(_anns) = annotations {
Vec::new()
} else {
Vec::new()
}
}
fn triple_expr2rbe(
&self,
triple_expr: &ast::TripleExpr,
compiled_schema: &mut SchemaIR,
current_table: &mut RbeTable<Pred, Node, ShapeLabelIdx, SemanticActionContext>,
source_iri: &IriS,
) -> CResult<Rbe<Component>> {
let mut visited_refs = Vec::new();
self.triple_expr2rbe_with_refs(
triple_expr,
compiled_schema,
current_table,
source_iri,
&mut visited_refs,
)
}
fn triple_expr2rbe_with_refs(
&self,
triple_expr: &ast::TripleExpr,
compiled_schema: &mut SchemaIR,
current_table: &mut RbeTable<Pred, Node, ShapeLabelIdx, SemanticActionContext>,
source_iri: &IriS,
visited_refs: &mut Vec<ast::TripleExprLabel>,
) -> CResult<Rbe<Component>> {
match triple_expr {
ast::TripleExpr::EachOf {
id: _,
expressions,
min,
max,
sem_acts,
annotations: _,
} => {
let mut cs = Vec::new();
for e in expressions {
let c = self.triple_expr2rbe_with_refs(
&e.te,
compiled_schema,
current_table,
source_iri,
visited_refs,
)?;
cs.push(c)
}
let card = self.cnv_min_max(min, max)?;
if sem_acts.is_some() {
info!("Semantic actions in EachOf ignored: {sem_acts:?}");
let actions = self.cnv_sem_actions(sem_acts, &compiled_schema.prefixmap())?;
let _conds = self.sem_acts_to_conds(actions)?;
};
Ok(Self::mk_card_group(Rbe::and(cs), card))
},
ast::TripleExpr::OneOf {
id: _,
expressions,
min,
max,
sem_acts,
annotations: _,
} => {
let mut cs = Vec::new();
for e in expressions {
let c = self.triple_expr2rbe_with_refs(
&e.te,
compiled_schema,
current_table,
source_iri,
visited_refs,
)?;
cs.push(c)
}
let card = self.cnv_min_max(min, max)?;
if sem_acts.is_some() {
info!("Semantic actions in OneOf ignored: {sem_acts:?}");
}
Ok(Self::mk_card_group(Rbe::or(cs), card))
},
ast::TripleExpr::TripleConstraint {
id: _,
negated: _,
inverse: _,
predicate,
value_expr,
min,
max,
sem_acts,
annotations: _,
} => {
let min = self.cnv_min(min)?;
let max = self.cnv_max(max)?;
let iri = Self::cnv_predicate(predicate)?;
let actions = self.cnv_sem_actions(sem_acts, &compiled_schema.prefixmap())?;
let (cond, _display) = self.value_expr2match_cond(value_expr, actions, compiled_schema, source_iri)?;
let component = current_table.add_component(iri, &cond);
Ok(Rbe::symbol(component, min.value, max))
},
ast::TripleExpr::Ref(r) => {
if visited_refs.contains(r) {
return Err(Box::new(SchemaIRError::Internal {
msg: format!("Cyclic TripleExprRef detected while compiling: {r}"),
}));
}
let referenced = self.triple_expr_labels.get(r).cloned().ok_or_else(|| {
Box::new(SchemaIRError::Internal {
msg: format!("TripleExprRef not found in schema imports closure: {r}"),
})
})?;
visited_refs.push(r.clone());
let rbe = self.triple_expr2rbe_with_refs(
&referenced,
compiled_schema,
current_table,
source_iri,
visited_refs,
)?;
visited_refs.pop();
Ok(rbe)
},
}
}
fn cnv_sem_actions(&self, sem_acts: &Option<Vec<ast::SemAct>>, prefixmap: &PrefixMap) -> CResult<Vec<SemAct>> {
if let Some(actions) = sem_acts {
actions.iter().map(|a| self.cnv_sem_action(a, prefixmap)).collect()
} else {
Ok(Vec::new())
}
}
fn cnv_sem_action(&self, sem_act: &ast::SemAct, prefixmap: &PrefixMap) -> CResult<SemAct> {
let iri_s = cnv_iri_ref(&sem_act.name(), prefixmap)?;
Ok(SemAct::new(iri_s, sem_act.code()))
}
fn cnv_predicate(predicate: &IriRef) -> CResult<Pred> {
match predicate {
IriRef::Iri(iri) => Ok(Pred::from(iri.clone())),
IriRef::Prefixed { prefix, local } => Err(Box::new(SchemaIRError::Internal {
msg: format!("Cannot convert prefixed {prefix}:{local} to predicate without context"),
})),
}
}
fn cnv_min_max(&self, min: &Option<i32>, max: &Option<i32>) -> CResult<Cardinality> {
let min = self.cnv_min(min)?;
let max = self.cnv_max(max)?;
Ok(Cardinality::from(min, max))
}
fn mk_card_group(rbe: Rbe<Component>, card: Cardinality) -> Rbe<Component> {
match &card {
c if c.is_1_1() => rbe,
c if c.is_star() => Rbe::Star { value: Box::new(rbe) },
c if c.is_plus() => Rbe::Plus { value: Box::new(rbe) },
_c => Rbe::Repeat {
value: Box::new(rbe),
card,
},
}
}
fn cnv_min(&self, min: &Option<i32>) -> CResult<Min> {
match min {
Some(min) if *min < 0 => Err(Box::new(SchemaIRError::MinLessZero { min: *min })),
Some(min) => Ok(Min::from(*min)),
None => Ok(Min::from(1)),
}
}
fn cnv_max(&self, max: &Option<i32>) -> CResult<Max> {
match *max {
Some(-1) => Ok(Max::Unbounded),
Some(max) if max < -1 => Err(Box::new(SchemaIRError::MaxIncorrect { max })),
Some(max) => Ok(Max::from(max)),
None => Ok(Max::from(1)),
}
}
fn value_expr2match_cond(
&self,
value_expr: &Option<Box<ast::ShapeExpr>>,
actions: Vec<SemAct>,
compiled_schema: &mut SchemaIR,
source_iri: &IriS,
) -> CResult<(Cond, String)> {
let mut sem_actions_conds = self.sem_acts_to_conds(actions)?;
let (cond, display) = if let Some(se) = value_expr.as_deref() {
match se {
ast::ShapeExpr::NodeConstraint(nc) => self.cnv_node_constraint(
&nc.node_kind(),
&nc.datatype(),
&nc.xs_facet(),
&nc.values(),
&compiled_schema.prefixmap(),
),
ast::ShapeExpr::Ref(sref) => {
let idx = self.ref2idx(sref, compiled_schema)?;
Ok((mk_cond_ref(idx), format!("ShapeRef {sref}")))
},
ast::ShapeExpr::Shape { .. } => {
let idx = compiled_schema.new_index(source_iri);
let se = self.compile_shape_expr(se, &idx, compiled_schema, source_iri)?;
compiled_schema.replace_shape(&idx, se.clone());
trace!("Returning SHAPE cond with idx {idx}");
Ok((mk_cond_ref(idx), format!("Shape {idx}")))
},
ast::ShapeExpr::ShapeAnd { shape_exprs } => {
let mut ands = Vec::new();
for shape_expr in shape_exprs {
let idx_se = compiled_schema.new_index(source_iri);
let se = self.compile_shape_expr(&shape_expr.se, &idx_se, compiled_schema, source_iri)?;
compiled_schema.replace_shape(&idx_se, se.clone());
ands.push(idx_se);
}
let and_se = ShapeExpr::ShapeAnd { exprs: ands.clone() };
let idx_and = compiled_schema.new_index(source_iri);
compiled_schema.replace_shape(&idx_and, and_se);
trace!("Returning AND cond with idx {idx_and}");
let display = format!(
"AND({})",
ands.iter().map(|i| i.to_string()).collect::<Vec<_>>().join(", ")
);
Ok((mk_cond_ref(idx_and), display))
},
ast::ShapeExpr::ShapeOr { shape_exprs } => {
let mut ors = Vec::new();
for se in shape_exprs {
let idx_se = compiled_schema.new_index(source_iri);
let se = self.compile_shape_expr(&se.se, &idx_se, compiled_schema, source_iri)?;
compiled_schema.replace_shape(&idx_se, se.clone());
ors.push(idx_se);
}
let or_se = ShapeExpr::ShapeOr { exprs: ors.clone() };
let display = format!(
"OR({})",
ors.iter().map(|i| i.to_string()).collect::<Vec<_>>().join(", ")
);
let idx_or = compiled_schema.new_index(source_iri);
compiled_schema.replace_shape(&idx_or, or_se);
Ok((mk_cond_ref(idx_or), display))
},
ast::ShapeExpr::ShapeNot { shape_expr } => {
let idx_shape_expr = compiled_schema.new_index(source_iri);
trace!(
"value_expr2matchcond: Compiling ShapeNot with {shape_expr:?}, idx_shape_expr {idx_shape_expr}"
);
let se = self.compile_shape_expr(&shape_expr.se, &idx_shape_expr, compiled_schema, source_iri)?;
compiled_schema.replace_shape(&idx_shape_expr, se.clone());
let display = format!("NOT {idx_shape_expr}");
let not_se = ShapeExpr::ShapeNot { expr: idx_shape_expr };
let idx_not = compiled_schema.new_index(source_iri);
compiled_schema.replace_shape(&idx_not, not_se);
trace!("Returning NOT cond with idx {idx_not}");
Ok((mk_cond_ref(idx_not), display))
},
ast::ShapeExpr::External => todo("value_expr2match_cond: ShapeExternal"),
}
} else {
Ok((MatchCond::single(SingleCond::new().with_name(".")), ".".to_string()))
}?;
if !sem_actions_conds.is_empty() {
sem_actions_conds.push(cond);
Ok((
MatchCond::and(sem_actions_conds),
format!("value expression {} + semantic actions", display),
))
} else {
Ok((cond, display))
}
}
fn get_preds_shape(&self, shape: &ast::Shape) -> CResult<Vec<Pred>> {
let mut visited_refs = Vec::new();
match shape.triple_expr() {
None => Ok(Vec::new()),
Some(te) => self.get_preds_triple_expr(&te, &mut visited_refs),
}
}
fn get_preds_triple_expr(
&self,
te: &ast::TripleExpr,
visited_refs: &mut Vec<ast::TripleExprLabel>,
) -> CResult<Vec<Pred>> {
match te {
ast::TripleExpr::EachOf { expressions, .. } => expressions
.iter()
.map(|te| self.get_preds_triple_expr(&te.te, visited_refs))
.collect::<CResult<Vec<Vec<Pred>>>>()
.map(|vss| vss.into_iter().flatten().collect()),
ast::TripleExpr::OneOf { expressions, .. } => expressions
.iter()
.map(|te| self.get_preds_triple_expr(&te.te, visited_refs))
.collect::<CResult<Vec<Vec<Pred>>>>()
.map(|vss| vss.into_iter().flatten().collect()),
ast::TripleExpr::TripleConstraint { predicate, .. } => {
let pred = iri_ref2iri_s(predicate);
Ok(vec![Pred::new(pred)])
},
ast::TripleExpr::Ref(r) => {
if visited_refs.contains(r) {
return Ok(Vec::new());
}
let referenced = self.triple_expr_labels.get(r).cloned().ok_or_else(|| {
Box::new(SchemaIRError::Internal {
msg: format!("TripleExprRef not found while collecting predicates: {r}"),
})
})?;
visited_refs.push(r.clone());
let preds = self.get_preds_triple_expr(&referenced, visited_refs)?;
visited_refs.pop();
Ok(preds)
},
}
}
fn collect_triple_expr_labels_schema(&mut self, schema_ast: &SchemaAST) -> CResult<()> {
if let Some(start) = schema_ast.start() {
self.collect_triple_expr_labels_shape_expr(&start)?;
}
if let Some(shape_decls) = schema_ast.shapes() {
for shape_decl in shape_decls {
self.collect_triple_expr_labels_shape_expr(&shape_decl.shape_expr)?;
}
}
Ok(())
}
fn collect_triple_expr_labels_shape_expr(&mut self, shape_expr: &ast::ShapeExpr) -> CResult<()> {
match shape_expr {
ast::ShapeExpr::ShapeOr { shape_exprs } | ast::ShapeExpr::ShapeAnd { shape_exprs } => {
for sew in shape_exprs {
self.collect_triple_expr_labels_shape_expr(&sew.se)?;
}
Ok(())
},
ast::ShapeExpr::ShapeNot { shape_expr } => self.collect_triple_expr_labels_shape_expr(&shape_expr.se),
ast::ShapeExpr::Shape(shape) => {
if let Some(expr) = shape.triple_expr() {
self.collect_triple_expr_labels_triple_expr(&expr)?;
}
Ok(())
},
ast::ShapeExpr::NodeConstraint(_) | ast::ShapeExpr::External | ast::ShapeExpr::Ref(_) => Ok(()),
}
}
fn collect_triple_expr_labels_triple_expr(&mut self, triple_expr: &ast::TripleExpr) -> CResult<()> {
match triple_expr {
ast::TripleExpr::EachOf { id, expressions, .. } | ast::TripleExpr::OneOf { id, expressions, .. } => {
self.register_triple_expr_label(id, triple_expr)?;
for tew in expressions {
self.collect_triple_expr_labels_triple_expr(&tew.te)?;
}
Ok(())
},
ast::TripleExpr::TripleConstraint { id, .. } => self.register_triple_expr_label(id, triple_expr),
ast::TripleExpr::Ref(_) => Ok(()),
}
}
fn register_triple_expr_label(
&mut self,
maybe_label: &Option<ast::TripleExprLabel>,
triple_expr: &ast::TripleExpr,
) -> CResult<()> {
if let Some(label) = maybe_label {
if self.triple_expr_labels.contains_key(label) {
return Err(Box::new(SchemaIRError::DuplicatedTripleExprLabel {
label: Box::new(label.clone()),
}));
}
self.triple_expr_labels.insert(label.clone(), triple_expr.clone());
}
Ok(())
}
fn sem_acts_to_conds(&self, actions: Vec<SemAct>) -> CResult<Vec<Cond>> {
actions
.iter()
.map(|a| {
let semantic_action_code = self
.semantic_actions_registry
.find_code(a.name())
.map_err(|e| Box::new(SchemaIRError::SemanticActionError { error: e.to_string() }))?;
Ok(mk_cond_sem_act(
a.name().clone(),
semantic_action_code,
a.code().cloned(),
))
})
.collect::<Result<Vec<_>, Box<SchemaIRError>>>()
}
}
fn iri_ref2iri_s(iri_ref: &IriRef) -> IriS {
match iri_ref {
IriRef::Iri(iri) => iri.clone(),
IriRef::Prefixed { prefix, local } => {
panic!("Compiling schema...found prefixed iri: {prefix}:{local}")
},
}
}
fn node_constraint2match_cond(
node_kind: &Option<ast::NodeKind>,
datatype: &Option<IriRef>,
xs_facet: &Option<Vec<ast::XsFacet>>,
values: &Option<ValueSet>,
prefixmap: &PrefixMap,
) -> CResult<(Cond, String)> {
let c1: Option<(Cond, String)> = node_kind.as_ref().map(node_kind2match_cond);
let c2 = datatype
.as_ref()
.map(|dt| datatype2match_cond(dt, prefixmap))
.transpose()?;
let c3 = xs_facet.as_ref().map(xs_facets2match_cond);
let c4 = values.as_ref().map(|vs| valueset2match_cond(vs.clone(), prefixmap));
let os = vec![c1, c2, c3, c4];
Ok(options2match_cond(os))
}
fn node_kind2match_cond(nodekind: &ast::NodeKind) -> (Cond, String) {
(mk_cond_nodekind(nodekind.clone()), format!("nodekind({nodekind})"))
}
fn datatype2match_cond(datatype: &IriRef, prefixmap: &PrefixMap) -> CResult<(Cond, String)> {
let iri = cnv_iri_ref(datatype, prefixmap)?;
let cond = mk_cond_datatype(&iri, prefixmap);
let str = cond.to_string();
Ok((cond, str))
}
fn xs_facets2match_cond(xs_facets: &Vec<ast::XsFacet>) -> (Cond, String) {
let mut conds = Vec::new();
for xs_facet in xs_facets {
conds.push(xs_facet2match_cond(xs_facet))
}
(MatchCond::And(conds), format!("xs_facets({xs_facets:?})"))
}
fn xs_facet2match_cond(xs_facet: &ast::XsFacet) -> Cond {
match xs_facet {
ast::XsFacet::StringFacet(sf) => string_facet_to_match_cond(sf),
ast::XsFacet::NumericFacet(nf) => numeric_facet_to_match_cond(nf),
}
}
fn string_facet_to_match_cond(sf: &ast::StringFacet) -> Cond {
match sf {
ast::StringFacet::Length(len) => mk_cond_length(*len),
ast::StringFacet::MinLength(len) => mk_cond_min_length(*len),
ast::StringFacet::MaxLength(len) => mk_cond_max_length(*len),
ast::StringFacet::Pattern(pat) => mk_cond_pattern(pat.regex(), pat.flags()),
}
}
fn numeric_facet_to_match_cond(nf: &ast::NumericFacet) -> Cond {
match nf {
ast::NumericFacet::MinInclusive(min) => mk_cond_min_inclusive(min.clone()),
ast::NumericFacet::MinExclusive(min) => mk_cond_min_exclusive(min.clone()),
ast::NumericFacet::MaxInclusive(max) => mk_cond_max_inclusive(max.clone()),
ast::NumericFacet::MaxExclusive(max) => mk_cond_max_exclusive(max.clone()),
ast::NumericFacet::TotalDigits(td) => mk_cond_total_digits(*td),
ast::NumericFacet::FractionDigits(fd) => mk_cond_fraction_digits(*fd),
}
}
fn valueset2match_cond(vs: ValueSet, prefixmap: &PrefixMap) -> (Cond, String) {
(
mk_cond_value_set(vs.clone(), prefixmap),
format!(
"[{}]",
vs.values()
.iter()
.map(|v| v.show_qualified(prefixmap))
.collect::<Vec<_>>()
.join(", ")
),
)
}
fn options2match_cond<T: IntoIterator<Item = Option<(Cond, String)>>>(os: T) -> (Cond, String) {
let vec: Vec<(Cond, String)> = os.into_iter().flatten().collect();
match &vec[..] {
[] => (MatchCond::empty(), ".".to_string()),
[(c, s)] => (c.clone(), s.clone()),
_ => (
MatchCond::And(vec.iter().map(|(c, _)| c.clone()).collect()),
vec.iter().map(|(_, s)| s.clone()).collect::<Vec<_>>().join(" AND "),
),
}
}
fn mk_cond_ref(idx: ShapeLabelIdx) -> Cond {
MatchCond::ref_(idx)
}
fn mk_cond_sem_act(
name: IriS,
semantic_action_code: Arc<dyn SemanticActionExtension + Send + Sync>,
code: Option<String>,
) -> Cond {
MatchCond::single(SingleCond::new().with_name(name.as_str()).with_cond(
move |_value: &Node, ctx: &SemanticActionContext| {
semantic_action_code
.run_action(code.as_deref(), ctx)
.map(|()| Pending::new())
.map_err(|e| RbeError::MsgError {
msg: format!("Semantic action error for {name}: {e}"),
})
},
))
}
fn mk_cond_datatype(datatype: &IriS, prefixmap: &PrefixMap) -> Cond {
let dt = datatype.clone();
MatchCond::single(
SingleCond::new()
.with_name(prefixmap.qualify(&dt).to_string().as_str())
.with_cond(
move |value: &Node, _ctx: &SemanticActionContext| match check_node_datatype(value, &dt) {
Ok(_) => Ok(Pending::new()),
Err(err) => Err(RbeError::MsgError {
msg: format!("Datatype error: {err}"),
}),
},
),
)
}
fn mk_cond_length(len: usize) -> Cond {
MatchCond::single(
SingleCond::new()
.with_name(format!("length({len})").as_str())
.with_cond(
move |value: &Node, _ctx: &SemanticActionContext| match check_node_length(value, len) {
Ok(_) => Ok(Pending::new()),
Err(err) => Err(RbeError::MsgError {
msg: format!("Length error: {err}"),
}),
},
),
)
}
fn mk_cond_min_inclusive(min: NumericLiteral) -> Cond {
let min_str = min.to_string();
MatchCond::single(
SingleCond::new()
.with_name(format!("minInclusive({min_str})").as_str())
.with_cond(move |value: &Node, _ctx: &SemanticActionContext| {
match check_node_min_inclusive(value, min.clone()) {
Ok(_) => Ok(Pending::new()),
Err(err) => Err(RbeError::MsgError {
msg: format!("MinInclusive: {err}"),
}),
}
}),
)
}
fn mk_cond_min_exclusive(min: NumericLiteral) -> Cond {
let min_str = min.to_string();
MatchCond::single(
SingleCond::new()
.with_name(format!("minExclusive({min_str})").as_str())
.with_cond(move |value: &Node, _ctx: &SemanticActionContext| {
match check_node_min_exclusive(value, min.clone()) {
Ok(_) => Ok(Pending::new()),
Err(err) => Err(RbeError::MsgError {
msg: format!("MinExclusive: {err}"),
}),
}
}),
)
}
fn mk_cond_total_digits(total: usize) -> Cond {
let total_str = total.to_string();
MatchCond::single(
SingleCond::new()
.with_name(format!("totalDigits({total_str})").as_str())
.with_cond(
move |value: &Node, _ctx: &SemanticActionContext| match check_node_total_digits(value, total) {
Ok(_) => Ok(Pending::new()),
Err(err) => Err(RbeError::MsgError {
msg: format!("MaxExclusive: {err}"),
}),
},
),
)
}
fn mk_cond_fraction_digits(total: usize) -> Cond {
let total_str = total.to_string();
MatchCond::single(
SingleCond::new()
.with_name(format!("fractionDigits({total_str})").as_str())
.with_cond(move |value: &Node, _ctx: &SemanticActionContext| {
match check_node_fraction_digits(value, total) {
Ok(_) => Ok(Pending::new()),
Err(err) => Err(RbeError::MsgError {
msg: format!("MaxExclusive: {err}"),
}),
}
}),
)
}
fn mk_cond_max_exclusive(max: NumericLiteral) -> Cond {
let max_str = max.to_string();
MatchCond::single(
SingleCond::new()
.with_name(format!("maxExclusive({max_str})").as_str())
.with_cond(move |value: &Node, _ctx: &SemanticActionContext| {
match check_node_max_exclusive(value, max.clone()) {
Ok(_) => Ok(Pending::new()),
Err(err) => Err(RbeError::MsgError {
msg: format!("MaxExclusive: {err}"),
}),
}
}),
)
}
fn mk_cond_max_inclusive(max: NumericLiteral) -> Cond {
let max_str = max.to_string();
MatchCond::single(
SingleCond::new()
.with_name(format!("maxInclusive({max_str})").as_str())
.with_cond(move |value: &Node, _ctx: &SemanticActionContext| {
match check_node_max_inclusive(value, max.clone()) {
Ok(_) => Ok(Pending::new()),
Err(err) => Err(RbeError::MsgError {
msg: format!("MaxInclusive: {err}"),
}),
}
}),
)
}
fn mk_cond_min_length(len: usize) -> Cond {
MatchCond::single(
SingleCond::new()
.with_name(format!("minLength({len})").as_str())
.with_cond(
move |value: &Node, _ctx: &SemanticActionContext| match check_node_min_length(value, len) {
Ok(_) => Ok(Pending::new()),
Err(err) => Err(RbeError::MsgError {
msg: format!("MinLength error: {err}"),
}),
},
),
)
}
fn mk_cond_max_length(len: usize) -> Cond {
MatchCond::simple(
format!("maxLength({len})").as_str(),
move |value: &Node, _ctx: &SemanticActionContext| match check_node_max_length(value, len) {
Ok(_) => Ok(Pending::new()),
Err(err) => Err(RbeError::MsgError {
msg: format!("MaxLength error: {err}"),
}),
},
)
}
fn mk_cond_nodekind(nodekind: ast::NodeKind) -> Cond {
MatchCond::single(
SingleCond::new()
.with_name(format!("nodekind({nodekind})").as_str())
.with_cond(move |value: &Node, _ctx: &SemanticActionContext| {
match check_node_node_kind(value, &nodekind) {
Ok(_) => Ok(Pending::empty()),
Err(err) => Err(RbeError::MsgError {
msg: format!("NodeKind Error: {err}"),
}),
}
}),
)
}
fn mk_cond_pattern(regex: &str, flags: Option<&str>) -> Cond {
let regex_str = format!("/{regex}/{}", flags.unwrap_or(""));
let regex = regex.to_string();
let flags = flags.map(|f| f.to_string());
MatchCond::single(SingleCond::new().with_name(regex_str.as_str()).with_cond(
move |value: &Node, _ctx: &SemanticActionContext| match check_pattern(value, ®ex, flags.as_deref()) {
Ok(_) => Ok(Pending::new()),
Err(err) => Err(RbeError::MsgError {
msg: format!("Pattern error: {err}"),
}),
},
))
}
fn iri_ref_2_shape_label(id: &IriRef) -> CResult<ShapeLabel> {
match id {
IriRef::Iri(iri) => Ok(ShapeLabel::Iri(iri.clone())),
IriRef::Prefixed { prefix, local } => Err(Box::new(SchemaIRError::IriRef2ShapeLabelError {
prefix: prefix.clone(),
local: local.clone(),
})),
}
}
fn mk_cond_value_set(value_set: ValueSet, prefixmap: &PrefixMap) -> Cond {
let cloned_prefixmap = prefixmap.clone();
MatchCond::single(
SingleCond::new()
.with_name(value_set.show_qualified(prefixmap).as_str())
.with_cond(move |node: &Node, _ctx: &SemanticActionContext| {
if value_set.check_value(node.as_object()) {
Ok(Pending::empty())
} else {
Err(RbeError::MsgError {
msg: format!(
"Values failed: {node} not in {}",
value_set.show_qualified(&cloned_prefixmap)
),
})
}
}),
)
}
fn create_value_set(values: &Vec<ast::ValueSetValue>, prefixmap: &PrefixMap) -> CResult<ValueSet> {
let mut vs = ValueSet::new();
for v in values {
let cvalue = cnv_value(v, prefixmap)?;
vs.add_value(cvalue)
}
Ok(vs)
}
fn cnv_value(v: &ast::ValueSetValue, prefixmap: &PrefixMap) -> CResult<ValueSetValue> {
match &v {
ast::ValueSetValue::IriStem { stem, .. } => {
let cnv_stem = cnv_iri_ref(stem, prefixmap)?;
Ok(ValueSetValue::IriStem { stem: cnv_stem })
},
ast::ValueSetValue::ObjectValue(ovw) => {
let ov = cnv_object_value(ovw, prefixmap)?;
Ok(ValueSetValue::ObjectValue(ov))
},
ast::ValueSetValue::Language { language_tag, .. } => Ok(ValueSetValue::Language {
language_tag: language_tag.clone(),
}),
ast::ValueSetValue::LiteralStem { stem, .. } => Ok(ValueSetValue::LiteralStem { stem: stem.to_string() }),
ast::ValueSetValue::LiteralStemRange { stem, exclusions } => {
let stem = cnv_string_or_wildcard(stem)?;
let exclusions = cnv_literal_exclusions(exclusions)?;
Ok(ValueSetValue::LiteralStemRange { stem, exclusions })
},
ast::ValueSetValue::IriStemRange { stem, exclusions } => {
let stem = cnv_iriref_or_wildcard(stem, prefixmap)?;
let exclusions = cnv_iri_exclusions(exclusions)?;
Ok(ValueSetValue::IriStemRange { stem, exclusions })
},
ast::ValueSetValue::LanguageStem { stem } => {
let stem = cnv_lang_or_wildcard(stem)?;
Ok(ValueSetValue::LanguageStem { stem })
},
ast::ValueSetValue::LanguageStemRange { stem, exclusions } => {
let stem = cnv_lang_or_wildcard(stem)?;
let exclusions = cnv_lang_exclusions(exclusions)?;
Ok(ValueSetValue::LanguageStemRange { stem, exclusions })
},
}
}
fn cnv_lang_or_wildcard(stem: &ast::LangOrWildcard) -> CResult<crate::ir::value_set_value::LangOrWildcard> {
match stem {
ast::LangOrWildcard::Lang(s) => Ok(crate::ir::value_set_value::LangOrWildcard::Lang(s.clone())),
ast::LangOrWildcard::Wildcard => Ok(crate::ir::value_set_value::LangOrWildcard::Wildcard {
type_: "Lang wildcard".to_string(),
}),
}
}
fn cnv_string_or_wildcard(stem: &ast::StringOrWildcard) -> CResult<crate::ir::value_set_value::StringOrWildcard> {
match stem {
ast::StringOrWildcard::String(s) => Ok(crate::ir::value_set_value::StringOrWildcard::String(s.to_string())),
ast::StringOrWildcard::Wildcard => {
Ok(crate::ir::value_set_value::StringOrWildcard::Wildcard { type_: "".to_string() })
},
}
}
fn cnv_iriref_or_wildcard(
stem: &ast::IriRefOrWildcard,
prefixmap: &PrefixMap,
) -> CResult<crate::ir::value_set_value::IriOrWildcard> {
match stem {
ast::IriRefOrWildcard::IriRef(iri) => {
let cnv_iri = cnv_iri_ref(iri, prefixmap)?;
Ok(crate::ir::value_set_value::IriOrWildcard::Iri(cnv_iri))
},
ast::IriRefOrWildcard::Wildcard => {
Ok(crate::ir::value_set_value::IriOrWildcard::Wildcard { type_: "".to_string() })
},
}
}
fn cnv_literal_exclusions(
exclusions: &Option<Vec<ast::literal_exclusion::LiteralExclusion>>,
) -> CResult<Option<Vec<ir::exclusion::LiteralExclusion>>> {
match exclusions {
None => Ok(None),
Some(exs) => {
let mut rs = Vec::new();
for ex in exs {
let cnv_ex = cnv_literal_exclusion(ex)?;
rs.push(cnv_ex);
}
Ok(Some(rs))
},
}
}
fn cnv_iri_exclusions(
exclusions: &Option<Vec<ast::iri_exclusion::IriExclusion>>,
) -> CResult<Option<Vec<ir::exclusion::IriExclusion>>> {
match exclusions {
None => Ok(None),
Some(exs) => {
let mut rs = Vec::new();
for ex in exs {
let cnv_ex = cnv_iri_exclusion(ex)?;
rs.push(cnv_ex);
}
Ok(Some(rs))
},
}
}
fn cnv_lang_exclusions(
exclusions: &Option<Vec<ast::language_exclusion::LanguageExclusion>>,
) -> CResult<Option<Vec<ir::exclusion::LanguageExclusion>>> {
match exclusions {
None => Ok(None),
Some(exs) => {
let mut rs = Vec::new();
for ex in exs {
let cnv_ex = cnv_language_exclusion(ex)?;
rs.push(cnv_ex);
}
Ok(Some(rs))
},
}
}
fn cnv_literal_exclusion(
le: &ast::literal_exclusion::LiteralExclusion,
) -> CResult<crate::ir::exclusion::LiteralExclusion> {
match le {
ast::literal_exclusion::LiteralExclusion::Literal(s) => {
Ok(crate::ir::exclusion::LiteralExclusion::Literal(s.to_string()))
},
ast::literal_exclusion::LiteralExclusion::LiteralStem(s) => {
Ok(crate::ir::exclusion::LiteralExclusion::LiteralStem(s.to_string()))
},
}
}
fn cnv_iri_exclusion(le: &IriExclusion) -> CResult<crate::ir::exclusion::IriExclusion> {
match le {
ast::iri_exclusion::IriExclusion::Iri(s) => {
let iri_s = iri_ref2iri_s(s);
Ok(crate::ir::exclusion::IriExclusion::Iri(iri_s))
},
ast::iri_exclusion::IriExclusion::IriStem(s) => {
let iri_s = iri_ref2iri_s(s);
Ok(crate::ir::exclusion::IriExclusion::IriStem(iri_s))
},
}
}
fn cnv_language_exclusion(
le: &ast::language_exclusion::LanguageExclusion,
) -> CResult<crate::ir::exclusion::LanguageExclusion> {
match le {
ast::language_exclusion::LanguageExclusion::Language(s) => {
Ok(crate::ir::exclusion::LanguageExclusion::Language(s.clone()))
},
ast::language_exclusion::LanguageExclusion::LanguageStem(s) => {
Ok(crate::ir::exclusion::LanguageExclusion::LanguageStem(s.clone()))
},
}
}
fn cnv_object_value(ov: &ast::ObjectValue, prefixmap: &PrefixMap) -> CResult<ObjectValue> {
match ov {
ast::ObjectValue::IriRef(ir) => {
let iri = cnv_iri_ref(ir, prefixmap)?;
Ok(ObjectValue::IriRef(iri))
},
ast::ObjectValue::Literal(lit) => Ok(ObjectValue::ObjectLiteral(lit.clone())),
}
}
fn check_pattern(node: &Node, regex: &str, flags: Option<&str>) -> CResult<()> {
let lexical_form = match node.as_object() {
Object::Literal(lit) => Ok(lit.lexical_form()),
Object::BlankNode(b) => Ok(b.clone()),
_ => Err(Box::new(SchemaIRError::PatternNodeNotLiteral {
node: node.to_string(),
regex: regex.to_string(),
flags: flags.map(|f| f.to_string()),
})),
}?;
if let Ok(re) = regex::Regex::new(regex) {
if re.is_match(&lexical_form) {
Ok(())
} else {
Err(Box::new(SchemaIRError::PatternError {
regex: regex.to_string(),
flags: flags.unwrap_or("").to_string(),
lexical_form: lexical_form.clone(),
}))
}
} else {
Err(Box::new(SchemaIRError::InvalidRegex {
regex: regex.to_string(),
}))
}
}
fn check_node_node_kind(node: &Node, nk: &ast::NodeKind) -> CResult<()> {
match (nk, node.as_object()) {
(ast::NodeKind::Iri, Object::Iri { .. }) => Ok(()),
(ast::NodeKind::Iri, _) => Err(Box::new(SchemaIRError::NodeKindIri { node: node.clone() })),
(ast::NodeKind::BNode, Object::BlankNode(_)) => Ok(()),
(ast::NodeKind::BNode, _) => Err(Box::new(SchemaIRError::NodeKindBNode { node: node.clone() })),
(ast::NodeKind::Literal, Object::Literal(_)) => Ok(()),
(ast::NodeKind::Literal, _) => Err(Box::new(SchemaIRError::NodeKindLiteral { node: node.clone() })),
(ast::NodeKind::NonLiteral, Object::BlankNode(_)) => Ok(()),
(ast::NodeKind::NonLiteral, Object::Iri { .. }) => Ok(()),
(ast::NodeKind::NonLiteral, _) => Err(Box::new(SchemaIRError::NodeKindNonLiteral { node: node.clone() })),
}
}
fn check_node_datatype(node: &Node, dt: &IriS) -> CResult<()> {
let object = node.as_checked_object().map_err(|e| {
Box::new(SchemaIRError::Internal {
msg: format!("check_node_datatype: as_checked_object error: {e}"),
})
})?;
match object {
Object::Literal(sliteral) => check_literal_datatype(&sliteral, dt, node),
Object::Iri(_) | Object::BlankNode(_) | Object::Triple { .. } => {
Err(Box::new(SchemaIRError::DatatypeNoLiteral {
expected: Box::new(dt.clone()),
node: Box::new(node.clone()),
}))
},
}
}
fn check_literal_datatype(sliteral: &ConcreteLiteral, expected: &IriS, node: &Node) -> CResult<()> {
let checked_literal = sliteral.clone().into_checked_literal().map_err(|e| {
Box::new(SchemaIRError::Internal {
msg: format!("check_literal_datatype: into_checked_literal error: {e}"),
})
})?;
match checked_literal {
ConcreteLiteral::WrongDatatypeLiteral {
lexical_form,
datatype,
error,
} => Err(Box::new(SchemaIRError::WrongDatatypeLiteralMatch {
datatype: datatype.clone(),
error: error.clone(),
expected: expected.clone(),
lexical_form: lexical_form.to_string(),
})),
_ => {
let node_dt = checked_literal.datatype();
let node_dt_iri = node_dt.get_iri().map_err(|e| {
Box::new(SchemaIRError::CheckLiteralDatatypeCnvIriRef2IriError {
iri_ref: node_dt.clone(),
error: e.to_string(),
})
})?;
if node_dt_iri == expected {
Ok(())
} else {
Err(Box::new(SchemaIRError::DatatypeDontMatch {
expected: expected.clone(),
found: node_dt,
lexical_form: node.to_string(),
}))
}
},
}
}
fn check_node_length(node: &Node, len: usize) -> CResult<()> {
debug!("check_node_length: {node:?} length: {len}");
let node_length = node.length();
if node_length == len {
Ok(())
} else {
Err(Box::new(SchemaIRError::LengthError {
expected: len,
found: node_length,
node: format!("{node}"),
}))
}
}
fn check_node_min_inclusive(node: &Node, min: NumericLiteral) -> CResult<()> {
trace!("check_node_min_inclusive: {node:?} min_inclusive: {min}");
let node_object = node.as_checked_object().map_err(|e| {
Box::new(SchemaIRError::Internal {
msg: format!("check_node_min_inclusive: as_checked_object error: {e}"),
})
})?;
let node_num = node_object.numeric_value().ok_or_else(|| {
Box::new(SchemaIRError::Internal {
msg: "check_node_min_inclusive: as_numeric error".to_string(),
})
})?;
if !node_num.less_than(&min) {
Ok(())
} else {
Err(Box::new(SchemaIRError::MinInclusiveError {
expected: min.clone(),
found: node_num,
node: node.to_string(),
}))
}
}
fn check_node_min_exclusive(node: &Node, min: NumericLiteral) -> CResult<()> {
trace!("check_node_min_exclusive: {node:?} min_exclusive: {min}");
let node_object = node.as_checked_object().map_err(|e| {
Box::new(SchemaIRError::Internal {
msg: format!("check_node_min_exclusive: as_checked_object error: {e}"),
})
})?;
let node_num = node_object.numeric_value().ok_or_else(|| {
Box::new(SchemaIRError::Internal {
msg: "check_node_min_exclusive: as_numeric error".to_string(),
})
})?;
if !node_num.less_than_or_eq(&min) {
Ok(())
} else {
Err(Box::new(SchemaIRError::MinExclusiveError {
expected: min.clone(),
found: node_num,
node: node.to_string(),
}))
}
}
fn check_node_total_digits(node: &Node, total: usize) -> CResult<()> {
trace!("check_node_total_digits: {node:?} total: {total}");
let node_object = node.as_checked_object().map_err(|e| {
Box::new(SchemaIRError::Internal {
msg: format!("check_node_total_digits: as_checked_object error: {e}"),
})
})?;
let node_num = node_object.numeric_value().ok_or_else(|| {
Box::new(SchemaIRError::Internal {
msg: "check_node_total_digits: as_numeric error".to_string(),
})
})?;
if let Some(num_digits) = node_num.total_digits() {
trace!("check_node_total_digits: node total digits: {num_digits}");
if num_digits <= total {
trace!("check_node_total_digits: OK {num_digits} <= {total} node [{node_num}]");
Ok(())
} else {
trace!("check_node_total_digits: Failed {num_digits} > {total} node [{node_num}]");
Err(Box::new(SchemaIRError::TotalDigitsError {
expected: total,
found: node_num,
node: node.to_string(),
}))
}
} else {
trace!("check_node_total_digits: node has no total digits");
Err(Box::new(SchemaIRError::TotalDigitsError {
expected: total,
found: node_num,
node: node.to_string(),
}))
}
}
fn check_node_fraction_digits(node: &Node, fd: usize) -> CResult<()> {
trace!("check_node_fraction_digits: {node:?} total: {fd}");
let node_object = node.as_checked_object().map_err(|e| {
Box::new(SchemaIRError::Internal {
msg: format!("check_node_fraction_digits: as_checked_object error: {e}"),
})
})?;
let node_num = node_object.numeric_value().ok_or_else(|| {
Box::new(SchemaIRError::Internal {
msg: "check_node_fraction_digits: as_numeric error".to_string(),
})
})?;
if let Some(num_fd) = node_num.fraction_digits() {
trace!("check_node_fraction_digits: node fraction digits: {num_fd}");
if num_fd <= fd {
trace!("check_node_fraction_digits: OK {fd:?} > Fraction digits of {node_num:?} = {num_fd}",);
Ok(())
} else {
trace!("check_node_fraction_digits: Failed {fd} <= fraction digits of {node_num} {num_fd}",);
Err(Box::new(SchemaIRError::FractionDigitsError {
expected: fd,
found: node_num,
node: node.to_string(),
}))
}
} else {
trace!("check_node_fraction_digits: node has no fraction digits");
Err(Box::new(SchemaIRError::FractionDigitsError {
expected: fd,
found: node_num,
node: node.to_string(),
}))
}
}
fn check_node_max_exclusive(node: &Node, max: NumericLiteral) -> CResult<()> {
trace!("check_node_max_exclusive: {node:?} max_exclusive: {max:?}");
let node_object = node.as_checked_object().map_err(|e| {
Box::new(SchemaIRError::Internal {
msg: format!("check_node_max_exclusive: as_checked_object error: {e}"),
})
})?;
let node_num = node_object.numeric_value().ok_or_else(|| {
Box::new(SchemaIRError::Internal {
msg: "check_node_min_exclusive: as_numeric error".to_string(),
})
})?;
if node_num.less_than(&max) {
trace!("check_node_max_exclusive: OK {node_num:?} < {max:?}");
Ok(())
} else {
trace!("check_node_max_exclusive: Failed {node_num} not less than {max}");
Err(Box::new(SchemaIRError::MinExclusiveError {
expected: max.clone(),
found: node_num,
node: node.to_string(),
}))
}
}
fn check_node_max_inclusive(node: &Node, max: NumericLiteral) -> CResult<()> {
trace!("check_node_max_inclusive: {node:?} max_inclusive: {max}");
let node_object = node.as_checked_object().map_err(|e| {
Box::new(SchemaIRError::Internal {
msg: format!("check_node_max_inclusive: as_checked_object error: {e}"),
})
})?;
let node_num = node_object.numeric_value().ok_or_else(|| {
Box::new(SchemaIRError::Internal {
msg: "check_node_max_inclusive: as_numeric error".to_string(),
})
})?;
if node_num.less_than_or_eq(&max) {
Ok(())
} else {
Err(Box::new(SchemaIRError::MaxInclusiveError {
expected: max.clone(),
found: node_num,
node: node.to_string(),
}))
}
}
fn check_node_min_length(node: &Node, len: usize) -> CResult<()> {
debug!("check_node_min_length: {node:?} min_length: {len}");
let node_length = node.length();
if node_length >= len {
Ok(())
} else {
Err(Box::new(SchemaIRError::MinLengthError {
expected: len,
found: node_length,
node: format!("{node}"),
}))
}
}
fn check_node_max_length(node: &Node, len: usize) -> CResult<()> {
debug!("check_node_max_length: {node:?} max_length: {len}");
let node_length = node.length();
if node_length <= len {
Ok(())
} else {
Err(Box::new(SchemaIRError::MaxLengthError {
expected: len,
found: node_length,
node: format!("{node}"),
}))
}
}
fn todo<A>(str: &str) -> CResult<A> {
panic!("TODO: {str}");
}
fn cnv_iri_ref(iri: &IriRef, prefixmap: &PrefixMap) -> Result<IriS, Box<SchemaIRError>> {
match iri {
IriRef::Iri(iri) => Ok(iri.clone()),
IriRef::Prefixed { prefix, local } => prefixmap.resolve_prefix_local(prefix, local).map_err(|e| {
Box::new(SchemaIRError::CnvIriRefError {
prefix: prefix.clone(),
local: local.clone(),
error: e.to_string(),
})
}),
}
}
pub fn find_schema_rotating_formats(
iri: &IriS,
formats: Vec<ShExFormat>,
base: Option<&IriS>,
) -> Result<SchemaAST, Box<SchemaIRError>> {
let mut errors = Vec::new();
for format in &formats {
trace!("Trying format {format} for IRI {iri} with base {base:?}");
match get_schema_from_iri(iri, format, base) {
Err(e) => {
errors.push((format.clone(), e));
},
Ok(schema) => return Ok(schema),
}
}
Err(Box::new(SchemaIRError::SchemaFromIriRotatingFormats {
iri: iri.clone(),
errors,
}))
}
pub fn get_schema_from_iri(
iri: &IriS,
format: &ShExFormat,
base: Option<&IriS>,
) -> Result<SchemaAST, Box<SchemaIRError>> {
let candidates = candidates(iri, base, format).map_err(|e| {
Box::new(SchemaIRError::CandidatesError {
iri: iri.clone(),
error: e.to_string(),
})
})?;
match format {
ShExFormat::ShExC => {
let content = find_content_from_iris(candidates, base)?;
let schema = ShExParser::parse(content.as_str(), Some(iri.clone()), iri).map_err(|e| {
Box::new(SchemaIRError::ShExCError {
iri: iri.clone(),
error: e.to_string(),
})
})?;
Ok(schema)
},
ShExFormat::ShExJ => {
let content = find_content_from_iris(candidates, base)?;
let schema = SchemaAST::from_reader(content.as_bytes()).map_err(|e| {
Box::new(SchemaIRError::ShExJError {
iri: iri.clone(),
error: e.to_string(),
})
})?;
Ok(schema)
},
ShExFormat::RDFFormat(_) => {
todo!()
},
}
}
pub fn candidates(iri: &IriS, base: Option<&IriS>, format: &ShExFormat) -> Result<Vec<IriS>, IriSError> {
let mut candidates = vec![iri.clone()];
let extended_iris: Result<Vec<IriS>, IriSError> = format
.extensions()
.into_iter()
.map(|ext| {
let ext_iri = format!("{}.{ext}", iri.as_str());
IriS::from_str_base_iri(&ext_iri, base)
})
.collect();
let extended = extended_iris?;
candidates.extend(extended);
Ok(candidates)
}
fn find_content_from_iris(iris: Vec<IriS>, base: Option<&IriS>) -> Result<String, Box<SchemaIRError>> {
find_first_ok(iris, |iri| get_content(iri, base))
.map_err(|errs| Box::new(SchemaIRError::FindingContentFromIrisError { errors: errs }))
.map(|(content, _)| content)
}
fn get_content(iri: IriS, base: Option<&IriS>) -> Result<String, SchemaIRError> {
iri.dereference(base).map_err(|e| SchemaIRError::DereferencingIri {
iri: iri.clone(),
error: e.to_string(),
})
}
fn find_first_ok<A, B, E, F>(vec: Vec<A>, f: F) -> Result<(B, Vec<E>), Vec<E>>
where
F: Fn(A) -> Result<B, E>,
{
let mut errors = Vec::new();
for item in vec {
match f(item) {
Ok(b) => return Ok((b, errors)),
Err(e) => errors.push(e),
}
}
Err(errors)
}