Skip to content

Commit

Permalink
Repairing behaviour of SHACL2ShEx
Browse files Browse the repository at this point in the history
  • Loading branch information
labra committed Jan 1, 2025
1 parent fa89238 commit 69f53dd
Show file tree
Hide file tree
Showing 5 changed files with 83 additions and 36 deletions.
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,16 @@

## Current changes without release yet

## [v0.1.59] - 2025-01-01

- Fixes bug in feature added to solve issue #227 for local files which are relative that it didn't generate an absolute IRI. Now it does.
- Added option to SHACL2ShEx converter to optionally add `rdf:type` declaration for each `sh:targetClass` declaration. Previously, this behaviour was not optional and now it can be disabled.
- Fixes option to generate `rdf:type` for `sh:targetClass` declarations when there are more than one (previously it generated one rdf:type for each target class, and not it generates a value set).

## [v0.1.58] - 2024-12-31

- Solves issue #227 to automatically generate a base URL from the local file name or URL.

## [v0.1.57] - 2024-11-14

- Simple release to bump a new version that solve a issue with pyrudof in Google Colab
Expand Down
20 changes: 20 additions & 0 deletions examples/shacl/relative_uris.ttl
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
@prefix : <#> .
@prefix dc: <http://purl.org/dc/terms/> .
@prefix owl: <http://www.w3.org/2002/07/owl#> .
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
@prefix schema: <http://schema.org/> .
@prefix sh: <http://www.w3.org/ns/shacl#> .

:Resource a sh:NodeShape ;
rdfs:label "Shape of a resource" ;
sh:targetClass schema:Person, schema:Event ;
sh:property [
sh:path schema:name ;
rdfs:label "name" ;
] ;
sh:property [
sh:path schema:license ;
rdfs:label "license" ;
] .

12 changes: 9 additions & 3 deletions rudof_cli/src/input_spec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -100,10 +100,16 @@ impl InputSpec {
pub fn guess_base(&self) -> Result<String, InputSpecError> {
match self {
InputSpec::Path(path) => {
let url: Url =
Url::from_file_path(path).map_err(|_| InputSpecError::GuessBaseFromPath {
path: path.to_path_buf(),
let absolute_path =
fs::canonicalize(path).map_err(|err| InputSpecError::AbsolutePathError {
path: path.to_string_lossy().to_string(),
error: err,
})?;
let url: Url = Url::from_file_path(absolute_path).map_err(|_| {
InputSpecError::GuessBaseFromPath {
path: path.to_path_buf(),
}
})?;
Ok(url.to_string())
}
InputSpec::Stdin => Ok("stdin://".to_string()),
Expand Down
61 changes: 28 additions & 33 deletions shapes_converter/src/shacl_to_shex/shacl2shex.rs
Original file line number Diff line number Diff line change
Expand Up @@ -91,47 +91,53 @@ impl Shacl2ShEx {
}
let is_closed = None; // TODO: Check real value
let extra = None; // TODO: Check if we could find a way to obtain extras in SHACL ?
let te = if exprs.is_empty() {
let mut te = if exprs.is_empty() {
None
} else {
Some(TripleExpr::each_of(exprs))
};
let target_class_expr = self.convert_target_decls(shape.targets(), schema)?;
let ter = match (te, target_class_expr) {
(None, None) => None,
(None, Some(t)) => Some(t),
(Some(t), None) => Some(t),
(Some(t1), Some(t2)) => Some(self.merge_triple_exprs(&t1, &t2)),
};
let shape = ShExShape::new(is_closed, extra, ter);
if self.config.add_target_class() {
let target_class_expr = self.convert_target_decls(shape.targets(), schema)?;
te = match (te, target_class_expr) {
(None, None) => None,
(None, Some(t)) => Some(t),
(Some(t), None) => Some(t),
(Some(t1), Some(t2)) => Some(self.merge_triple_exprs(&t1, &t2)),
};
}
let shape = ShExShape::new(is_closed, extra, te);
Ok(ShapeExpr::shape(shape))
}

/// Collect targetClass declarations and add a rdf:type constraint for each
pub fn convert_target_decls(
&self,
targets: &Vec<Target>,
schema: &ShaclSchema,
) -> Result<Option<TripleExpr>, Shacl2ShExError> {
let mut tes = Vec::new();
let mut values = Vec::new();
for target in targets {
let te = self.target2triple_constraint(target, schema)?;
match te {
None => (),
Some(te) => tes.push(te),
if let Some(value) = self.target2value_set_value(target, schema)? {
values.push(value);
}
}
if tes.is_empty() {
Ok(None)
} else {
Ok(Some(TripleExpr::each_of(tes)))
}
let value_cls = ShapeExpr::node_constraint(NodeConstraint::new().with_values(values));
let tc = TripleExpr::triple_constraint(
None,
None,
IriRef::iri(IriS::rdf_type()),
Some(value_cls),
None,
None,
);
Ok(Some(tc))
}

pub fn target2triple_constraint(
pub fn target2value_set_value(
&self,
target: &Target,
_schema: &ShaclSchema,
) -> Result<Option<TripleExpr>, Shacl2ShExError> {
) -> Result<Option<ValueSetValue>, Shacl2ShExError> {
match target {
Target::TargetNode(_) => Ok(None),
Target::TargetClass(cls) => {
Expand All @@ -146,18 +152,7 @@ impl Shacl2ShEx {
literal: lit.clone(),
}),
}?;
let value_cls = ShapeExpr::node_constraint(
NodeConstraint::new().with_values(vec![value_set_value]),
);
let tc = TripleExpr::triple_constraint(
None,
None,
IriRef::iri(IriS::rdf_type()),
Some(value_cls),
None,
None,
);
Ok(Some(tc))
Ok(Some(value_set_value))
}
Target::TargetSubjectsOf(_) => Ok(None),
Target::TargetObjectsOf(_) => Ok(None),
Expand Down
16 changes: 16 additions & 0 deletions shapes_converter/src/shacl_to_shex/shacl2shex_config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,17 @@ use shacl_validation::shacl_config::ShaclConfig;
/// Defines the configuration of the converter
#[derive(Serialize, Deserialize, PartialEq, Debug, Clone, Default)]
pub struct Shacl2ShExConfig {
/// Starting shapes mode. Default: NonBNodes
pub starting_shapes_mode: Option<StartShapeMode>,

/// If true, embed blank nodes in the ShEx schema
pub embed_bnodes: Option<bool>,

/// SHACL configuration
pub shacl: Option<ShaclConfig>,

/// Add an `rdf:type` constraint for `sh:targetClass` declarations
pub add_target_class: Option<bool>,
}

impl Shacl2ShExConfig {
Expand All @@ -16,10 +24,18 @@ impl Shacl2ShExConfig {
Some(sm) => sm.clone(),
}
}

pub fn add_target_class(&self) -> bool {
match &self.add_target_class {
None => true,
Some(atc) => *atc,
}
}
}

#[derive(Serialize, Deserialize, PartialEq, Debug, Clone, Default)]
pub enum StartShapeMode {
/// Process shapes which are not blank nodes
#[default]
NonBNodes,
}

0 comments on commit 69f53dd

Please sign in to comment.