use std::collections::HashSet;
use crate::graph::rdf::{RdfStore, Term, TriplePattern};
use super::shape::{RDF, Shape, Target};
pub fn resolve_targets(shape: &Shape, data_graph: &RdfStore) -> Vec<Term> {
let mut focus_nodes: HashSet<Term> = HashSet::new();
for target in shape.targets() {
match target {
Target::Class(class) => {
resolve_class_target(class, data_graph, &mut focus_nodes);
}
Target::Node(node) => {
focus_nodes.insert(node.clone());
}
Target::SubjectsOf(predicate) => {
for triple in data_graph.triples_with_predicate(predicate) {
focus_nodes.insert(triple.subject().clone());
}
}
Target::ObjectsOf(predicate) => {
for triple in data_graph.triples_with_predicate(predicate) {
focus_nodes.insert(triple.object().clone());
}
}
}
}
if shape.targets().is_empty() && shape.id().is_iri() {
resolve_class_target(shape.id(), data_graph, &mut focus_nodes);
}
focus_nodes.into_iter().collect()
}
fn resolve_class_target(class: &Term, data_graph: &RdfStore, out: &mut HashSet<Term>) {
let rdf_type = Term::iri(RDF::TYPE);
let pattern = TriplePattern {
subject: None,
predicate: Some(rdf_type),
object: Some(class.clone()),
};
for triple in data_graph.find(&pattern) {
out.insert(triple.subject().clone());
}
}
#[cfg(test)]
mod tests {
use super::super::shape::{NodeShape, Severity};
use super::*;
use crate::graph::rdf::{RdfStore, Triple};
fn make_node_shape(id: &str, targets: Vec<Target>) -> Shape {
Shape::Node(NodeShape {
id: Term::iri(id),
targets,
property_shapes: Vec::new(),
constraints: Vec::new(),
deactivated: false,
severity: Severity::Violation,
messages: Vec::new(),
})
}
fn sample_data() -> RdfStore {
let store = RdfStore::new();
let rdf_type = Term::iri(RDF::TYPE);
let person = Term::iri("http://ex.org/Person");
let city = Term::iri("http://ex.org/City");
let likes = Term::iri("http://ex.org/likes");
store.insert(Triple::new(
Term::iri("http://ex.org/alix"),
rdf_type.clone(),
person.clone(),
));
store.insert(Triple::new(
Term::iri("http://ex.org/gus"),
rdf_type.clone(),
person.clone(),
));
store.insert(Triple::new(
Term::iri("http://ex.org/amsterdam"),
rdf_type,
city,
));
store.insert(Triple::new(
Term::iri("http://ex.org/alix"),
likes.clone(),
Term::iri("http://ex.org/amsterdam"),
));
store.insert(Triple::new(
Term::iri("http://ex.org/gus"),
likes,
Term::iri("http://ex.org/berlin"),
));
store
}
#[test]
fn target_class_resolves_instances() {
let data = sample_data();
let shape = make_node_shape(
"http://ex.org/S",
vec![Target::Class(Term::iri("http://ex.org/Person"))],
);
let nodes = resolve_targets(&shape, &data);
assert_eq!(nodes.len(), 2);
}
#[test]
fn target_node_returns_specified_node() {
let data = sample_data();
let shape = make_node_shape(
"http://ex.org/S",
vec![Target::Node(Term::iri("http://ex.org/alix"))],
);
let nodes = resolve_targets(&shape, &data);
assert_eq!(nodes.len(), 1);
assert!(nodes.contains(&Term::iri("http://ex.org/alix")));
}
#[test]
fn target_subjects_of() {
let data = sample_data();
let shape = make_node_shape(
"http://ex.org/S",
vec![Target::SubjectsOf(Term::iri("http://ex.org/likes"))],
);
let nodes = resolve_targets(&shape, &data);
assert_eq!(nodes.len(), 2); }
#[test]
fn target_objects_of() {
let data = sample_data();
let shape = make_node_shape(
"http://ex.org/S",
vec![Target::ObjectsOf(Term::iri("http://ex.org/likes"))],
);
let nodes = resolve_targets(&shape, &data);
assert_eq!(nodes.len(), 2); }
#[test]
fn implicit_class_target() {
let data = sample_data();
let shape = make_node_shape("http://ex.org/Person", vec![]);
let nodes = resolve_targets(&shape, &data);
assert_eq!(nodes.len(), 2); }
#[test]
fn multiple_targets_produce_union() {
let data = sample_data();
let shape = make_node_shape(
"http://ex.org/S",
vec![
Target::Class(Term::iri("http://ex.org/Person")),
Target::Node(Term::iri("http://ex.org/amsterdam")),
],
);
let nodes = resolve_targets(&shape, &data);
assert_eq!(nodes.len(), 3); }
#[test]
fn empty_target_set() {
let data = sample_data();
let shape = make_node_shape(
"http://ex.org/S",
vec![Target::Class(Term::iri("http://ex.org/NonExistent"))],
);
let nodes = resolve_targets(&shape, &data);
assert!(nodes.is_empty());
}
#[test]
fn target_with_named_graph() {
let store = RdfStore::new();
let graph = store.graph_or_create("http://ex.org/g1");
let rdf_type = Term::iri(RDF::TYPE);
let person = Term::iri("http://ex.org/Person");
graph.insert(Triple::new(
Term::iri("http://ex.org/alix"),
rdf_type,
person,
));
let shape = make_node_shape(
"http://ex.org/S",
vec![Target::Class(Term::iri("http://ex.org/Person"))],
);
let nodes = resolve_targets(&shape, &graph);
assert_eq!(nodes.len(), 1);
}
}