use crate::shacl_engine::engine::Engine;
use crate::shacl_engine::native::NativeEngine;
use crate::shape_validation::Validate;
use crate::store::Graph;
use crate::store::Store;
use crate::validate_error::ValidateError;
use crate::validation_report::report::ValidationReport;
use rudof_rdf::rdf_core::{NeighsRDF, RDFFormat};
#[cfg(not(feature = "sparql"))]
use rudof_rdf::rdf_impl::InMemoryGraph;
use shacl_ir::compiled::schema_ir::SchemaIR;
use std::fmt::Debug;
#[cfg(not(target_family = "wasm"))]
use std::path::Path;
use std::{
fmt::{Display, Formatter},
str::FromStr,
};
#[cfg(feature = "sparql")]
use {crate::shacl_engine::sparql::SparqlEngine, sparql_service::RdfData};
#[cfg(feature = "sparql")]
mod sparql;
#[cfg(feature = "sparql")]
pub use sparql::{EndpointValidation, RdfDataValidation};
#[derive(Copy, Clone, Debug, PartialEq, Default)]
pub enum ShaclValidationMode {
#[default]
Native,
#[cfg(feature = "sparql")]
Sparql,
}
impl Display for ShaclValidationMode {
fn fmt(&self, dest: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
match self {
ShaclValidationMode::Native => write!(dest, "native"),
#[cfg(feature = "sparql")]
ShaclValidationMode::Sparql => write!(dest, "sparql"),
}
}
}
impl FromStr for ShaclValidationMode {
type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s.to_lowercase().as_str() {
"native" => Ok(ShaclValidationMode::Native),
#[cfg(feature = "sparql")]
"sparql" => Ok(ShaclValidationMode::Sparql),
other => Err(format!("Unsupported SHACL validation mode: {}", other)),
}
}
}
pub trait ShaclProcessor<S: NeighsRDF + Debug> {
fn validate(&mut self, shapes_graph: &SchemaIR) -> Result<ValidationReport, Box<ValidateError>>;
}
pub(crate) fn do_validate<S: NeighsRDF + Debug>(
store: &S,
mut runner: Box<dyn Engine<S>>,
shapes_graph: &SchemaIR,
) -> Result<ValidationReport, Box<ValidateError>> {
runner.build_indexes(store)?;
let mut validation_results = Vec::new();
for (_, shape) in shapes_graph.iter_with_targets() {
tracing::debug!("ShaclProcessor.validate with shape {}", shape.id());
let results = shape.validate(store, &mut (*runner), None, Some(shape), shapes_graph)?;
validation_results.extend(results);
}
Ok(ValidationReport::new()
.with_results(validation_results)
.with_prefixmap(shapes_graph.prefix_map()))
}
pub struct GraphValidation {
store: Graph,
mode: ShaclValidationMode,
}
impl GraphValidation {
#[cfg(not(target_family = "wasm"))]
pub fn from_path<P: AsRef<Path>>(
data: P,
data_format: RDFFormat,
base: Option<&str>,
mode: ShaclValidationMode,
) -> Result<Self, Box<ValidateError>> {
let store = Graph::from_path(data.as_ref(), data_format, base)?;
Ok(GraphValidation { store, mode })
}
pub fn from_graph(graph: Graph, mode: ShaclValidationMode) -> GraphValidation {
GraphValidation { store: graph, mode }
}
}
#[cfg(feature = "sparql")]
impl ShaclProcessor<RdfData> for GraphValidation {
fn validate(&mut self, shapes_graph: &SchemaIR) -> Result<ValidationReport, Box<ValidateError>> {
let store = self.store.store();
let runner: Box<dyn Engine<RdfData>> = match self.mode {
ShaclValidationMode::Native => Box::new(NativeEngine::new()),
ShaclValidationMode::Sparql => Box::new(SparqlEngine::new()),
};
do_validate(store, runner, shapes_graph)
}
}
#[cfg(not(feature = "sparql"))]
impl ShaclProcessor<InMemoryGraph> for GraphValidation {
fn validate(&mut self, shapes_graph: &SchemaIR) -> Result<ValidationReport, Box<ValidateError>> {
let store = self.store.store();
let runner: Box<dyn Engine<InMemoryGraph>> = Box::new(NativeEngine::new());
do_validate(store, runner, shapes_graph)
}
}