Opt-in SHACL Core + SHACL-SPARQL (sh:sparql) validation over
[sparq_core::Graph]s. Core constraints run at the dictionary-id level via direct,
index-backed permutation scans (no SPARQL round-trip; terms materialise only at the
report boundary); sh:sparql routes its sh:select through sparq-engine. Reports:
conforms + per-result detail; to_turtle() / to_ntriples() (W3C report graph), to_json(), and to_text().
Like sparq-reason, this crate is isolated: no other sparq crate depends on it, so
the core engine and the default wasm bundle carry zero SHACL code. The browser/JS
consumer opts in through sparq-wasm's non-default shacl feature
(Store.validate(data, shapes, format), a drop-in for rdf-validate-shacl).
# fn main() -> Result<(), Box<dyn std::error::Error>> {
use sparq_core::Graph;
let data = Graph::load_str(r#"
@prefix ex: <http://example.org/> .
ex:alice a ex:Person ; ex:age "thirty" .
"#, "turtle")?;
let shapes = Graph::load_str(r#"
@prefix sh: <http://www.w3.org/ns/shacl#> .
@prefix ex: <http://example.org/> .
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
ex:PersonShape a sh:NodeShape ;
sh:targetClass ex:Person ;
sh:property [ sh:path ex:age ; sh:datatype xsd:integer ; sh:minCount 1 ] .
"#, "turtle")?;
let report = sparq_shacl::validate(&data, &shapes);
if !report.conforms {
eprintln!("{}", report.to_text()); // or report.to_turtle() for the report graph
}
// SHACL-1.2 default `conforms` fails on {Violation,Warning,Info}; the stricter toggle:
if report.conforms_violations_only() { /* fails on sh:Violation only */ }
# assert!(!report.conforms);
# Ok(()) }For many data graphs against one shapes graph, parse once with ShapesModel::parse(&shapes)
and call validate_with_model. CLI: cargo run -p sparq-shacl --example validate -- data.ttl shapes.ttl [--turtle].
- W3C core conformance — 98 / 98 (100%) of the core
sht:Validateentries of the official w3c/data-shapes suite (pinnedb6e73695);fetch-shacl-tests.shthencargo test -p sparq-shacl --test w3c_core. - SHACL 1.2 core constraints —
sh:memberShape/sh:uniqueMembers/sh:{min,max}ListLength/sh:uniqueValuesFor/sh:closed sh:ByTypes(sq-vg3y), disjunctive-listsh:datatype/sh:nodeKind/sh:class, path-valued comparands forsh:equals/sh:disjoint/sh:lessThan/sh:lessThanOrEquals,sh:subsetOf/sh:someValue/sh:singleLine/sh:rootClass, severity-thresholdconforms(shapes-graphsh:conformanceDisallowsoverrides the default {Violation,Warning,Info}, sq-5q76d),sh:reifierShape/sh:reificationRequiredover RDF-1.2 reifiers, therdf:dirLangStringsh:uniqueLangkey (sq-0mjfd), and per-constraint-statement reified-annotation overrides ({| sh:deactivated/message/severity … |}apply to ONLY that occurrence, sq-pb0wm). The full vendored 1.2 suite is gated by a two-sided ratchet (sq-6glcr): core / SPARQL (incl.sht:Failurerejection) / node-expr, in both feature states — gap map inresearch/shacl12-conformance-gap.md. - SHACL-SPARQL + custom §6 components —
sh:sparql(§5.2, results carrysh:sourceConstraint;$this/$valuepre-binding propagates into UNION branches, sibling joins and projecting sub-SELECTs, sq-mue75) and SPARQL-based constraint components (sh:ConstraintComponent+sh:parameter/sh:validator, §6), pinned by the W3Csparql/*sub-suites. - SHACL Advanced Features (opt-in
shacl-af) — a rule inference step (sh:rule/sh:values, not part ofvalidate):sh:TripleRule,sh:SPARQLRule, value rules, node expressions,sh:expression/sh:nodeByExpression;validate_with_domain(and its model variant) selects asserted facts ordata ∪ inferred.apply_rules_in_scope/expand_datasettake aGraphScopeand aDestination, keeping asserted named graphs (gh-6614). Off ⇒ none compiles. - Differential fuzzing — a deterministic SplitMix64 fuzzer (
tests/diff_fuzz.rs) cross-checks reports against pluggable reference engines (pySHACL, Jena SHACL, the Zazuko /rdf-validate-shaclNode engines);#[ignore]d, a nightly CI lane. - Full path + target support — all SHACL path forms (sequence / alternative /
inverse /
zeroOrMore·oneOrMore·zeroOrOne),sh:targetNode/Class/SubjectsOf/ObjectsOf+ implicit class targets, the SHACL-1.2 targetssh:targetWhere/sh:shape/sh:ShapeClass+ SPARQL-valued targets & value nodes (sh:select/sh:sparqlExpr, sq-rnkdh), plussh:severity/sh:message/sh:deactivated. - SHACL Compact Syntax parser (opt-in
scs) —parse_scs(text, base)/parse_scs_to_graph(text, base)turn a W3C SHACL Compact Syntax document into the same shapes triplesvalidateconsumes; round-trips 32/32 of the vendored W3C fixtures graph-isomorphically; zero new deps. Off ⇒ none compiles. - Shapes-graph assembly (opt-in
imports) —resolve_shapes_graph/resolve_imports:sh:shapesGraph+ cycle-guardedowl:importsclosure via a caller-supplied loader. Off ⇒ none compiles.
- How-to —
skills/shacl-validation/SKILL.md(the exhaustive supported-constraint list and the report shape). - API reference — docs.rs/sparq-shacl.
- Spec — W3C SHACL (Core, SPARQL, AF); the surface is pinned by
tests/. - Contribute —
AGENTS.md.
validate_strict returns Err(ShaclFailure) for an unsound SHACL-SPARQL pre-binding (MINUS/
VALUES/SERVICE / a sub-SELECT dropping $this / a BIND re-binding it, sq-0mjfd) and for
ill-formed shapes-graph constructs — the W3C-suite sht:Failure outcome: an unparsable
sh:path or pathless property shape, a non-xsd:integer count/length, a malformed SHACL list,
a non-node-kind sh:nodeKind, a non-IRI sh:severity, EITHER qualified parameter without the
other, ANY sh:entailment (no regime is supported, SHACL §3.4), or a PRESENT sh:select/
sh:sparqlExpr/shacl-af node expression that does not build — fail-closed relative to this
engine's parsers (sq-11a, sq-ehq4g, sq-c1v3e); construct-local checks, not a full
SHACL-of-SHACL pass. validate skips all of these leniently, surfacing each skip in
report.diagnostics (joining the sq-lz99x uncompilable-sh:pattern skip there). Out of scope:
$shapesGraph — see bd list -l area:sparq-shacl. Results are not deduplicated across
traversal routes (a nested shape reports once per parent route); re-entrant recursion conforms.
MIT.