use crate::{Literal, Statement, SubjectNode};
use rdftk_iri::IRIRef;
use rdftk_names::rdf;
use std::cell::RefCell;
use std::collections::HashMap;
use std::rc::Rc;
#[derive(Clone, Debug)]
pub struct Resource {
subject: SubjectNode,
predicates: HashMap<IRIRef, RefCell<Vec<ResourceObject>>>,
}
#[derive(Clone, Debug)]
pub struct Predicate {
name: IRIRef,
objects: Vec<ResourceObject>,
}
#[allow(clippy::large_enum_variant)]
#[derive(Clone, Debug)]
enum ResourceObject {
Resource(Resource),
Resources(Container<Resource>),
Literal(Literal),
Literals(Container<Literal>),
}
#[allow(clippy::large_enum_variant)]
#[derive(Clone, Debug)]
enum ContainerKind {
Alt,
Bag,
Seq,
Other(IRIRef),
}
#[derive(Clone, Debug)]
struct Container<T> {
kind: ContainerKind,
values: Vec<T>,
}
impl Predicate {
pub fn new(name: IRIRef) -> Self {
Self {
name,
objects: Default::default(),
}
}
pub fn property(&mut self, value: Literal) -> &mut Self {
self.objects.push(ResourceObject::Literal(value));
self
}
pub fn value_of(&mut self, value: Literal) -> &mut Self {
self.objects.push(ResourceObject::Literal(value));
self
}
pub fn literal(&mut self, value: Literal) -> &mut Self {
self.objects.push(ResourceObject::Literal(value));
self
}
pub fn property_alternatives(&mut self, values: &[Literal]) -> &mut Self {
self.objects.push(ResourceObject::Literals(Container {
kind: ContainerKind::Alt,
values: values.to_vec(),
}));
self
}
pub fn property_bag(&mut self, values: &[Literal]) -> &mut Self {
self.objects.push(ResourceObject::Literals(Container {
kind: ContainerKind::Bag,
values: values.to_vec(),
}));
self
}
pub fn property_sequence(&mut self, values: &[Literal]) -> &mut Self {
self.objects.push(ResourceObject::Literals(Container {
kind: ContainerKind::Seq,
values: values.to_vec(),
}));
self
}
pub fn property_container(&mut self, values: &[Literal], kind: IRIRef) -> &mut Self {
self.objects.push(ResourceObject::Literals(Container {
kind: ContainerKind::Other(kind),
values: values.to_vec(),
}));
self
}
pub fn resource_blank_named(&mut self, name: &str) -> &mut Self {
self.objects
.push(ResourceObject::Resource(Resource::blank_named(name)));
self
}
pub fn resource_named(&mut self, name: IRIRef) -> &mut Self {
self.objects
.push(ResourceObject::Resource(Resource::named(name)));
self
}
pub fn resource(&mut self, resource: Resource) -> &mut Self {
self.objects.push(ResourceObject::Resource(resource));
self
}
pub fn resource_alternatives(&mut self, values: &[Resource]) -> &mut Self {
self.objects.push(ResourceObject::Resources(Container {
kind: ContainerKind::Alt,
values: values.to_vec(),
}));
self
}
pub fn resource_bag(&mut self, values: &[Resource]) -> &mut Self {
self.objects.push(ResourceObject::Resources(Container {
kind: ContainerKind::Bag,
values: values.to_vec(),
}));
self
}
pub fn resource_sequence(&mut self, values: &[Resource]) -> &mut Self {
self.objects.push(ResourceObject::Resources(Container {
kind: ContainerKind::Seq,
values: values.to_vec(),
}));
self
}
pub fn resource_container(&mut self, values: &[Resource], kind: IRIRef) -> &mut Self {
self.objects.push(ResourceObject::Resources(Container {
kind: ContainerKind::Other(kind),
values: values.to_vec(),
}));
self
}
}
impl Into<Vec<Statement>> for Resource {
fn into(self) -> Vec<Statement> {
let mut sts = Vec::default();
flatten(&self, &mut sts);
sts
}
}
impl Into<Vec<Rc<Statement>>> for Resource {
fn into(self) -> Vec<Rc<Statement>> {
let sts: Vec<Statement> = self.into();
sts.iter().cloned().map(Rc::new).collect()
}
}
impl Resource {
pub fn new(subject: SubjectNode) -> Self {
Self {
subject,
predicates: Default::default(),
}
}
pub fn blank() -> Self {
Self {
subject: SubjectNode::blank(),
predicates: Default::default(),
}
}
pub fn blank_named(name: &str) -> Self {
Self {
subject: SubjectNode::blank_named(name),
predicates: Default::default(),
}
}
pub fn named(name: IRIRef) -> Self {
Self {
subject: SubjectNode::named(name),
predicates: Default::default(),
}
}
#[inline]
pub fn is_a_resource(&self) -> bool {
self.subject.is_iri()
}
#[inline]
pub fn is_an_individual(&self) -> bool {
self.predicates.keys().any(|p| p == rdf::a_type())
}
pub fn predicate(&mut self, predicate: Predicate) -> &mut Self {
for object in predicate.objects.into_iter() {
self.insert(predicate.name.clone(), object);
}
self
}
pub fn property(&mut self, predicate: IRIRef, value: Literal) -> &mut Self {
self.literal(predicate, value)
}
pub fn value_of(&mut self, predicate: IRIRef, value: Literal) -> &mut Self {
self.literal(predicate, value)
}
pub fn literal(&mut self, predicate: IRIRef, value: Literal) -> &mut Self {
self.insert(predicate, ResourceObject::Literal(value))
}
pub fn property_alternatives(&mut self, predicate: IRIRef, values: &[Literal]) -> &mut Self {
self.insert(
predicate,
ResourceObject::Literals(Container {
kind: ContainerKind::Alt,
values: values.to_vec(),
}),
)
}
pub fn property_bag(&mut self, predicate: IRIRef, values: &[Literal]) -> &mut Self {
self.insert(
predicate,
ResourceObject::Literals(Container {
kind: ContainerKind::Bag,
values: values.to_vec(),
}),
)
}
pub fn property_sequence(&mut self, predicate: IRIRef, values: &[Literal]) -> &mut Self {
self.insert(
predicate,
ResourceObject::Literals(Container {
kind: ContainerKind::Seq,
values: values.to_vec(),
}),
)
}
pub fn property_container(
&mut self,
predicate: IRIRef,
values: &[Literal],
kind: IRIRef,
) -> &mut Self {
self.insert(
predicate,
ResourceObject::Literals(Container {
kind: ContainerKind::Other(kind),
values: values.to_vec(),
}),
)
}
pub fn resource_blank_named(&mut self, predicate: IRIRef, name: &str) -> &mut Self {
self.insert(
predicate,
ResourceObject::Resource(Resource::blank_named(name)),
)
}
pub fn resource_named(&mut self, predicate: IRIRef, name: IRIRef) -> &mut Self {
self.insert(predicate, ResourceObject::Resource(Resource::named(name)))
}
pub fn resource(&mut self, predicate: IRIRef, resource: Resource) -> &mut Self {
self.insert(predicate, ResourceObject::Resource(resource))
}
pub fn resource_alternatives(&mut self, predicate: IRIRef, values: &[Resource]) -> &mut Self {
self.insert(
predicate,
ResourceObject::Resources(Container {
kind: ContainerKind::Alt,
values: values.to_vec(),
}),
)
}
pub fn resource_bag(&mut self, predicate: IRIRef, values: &[Resource]) -> &mut Self {
self.insert(
predicate,
ResourceObject::Resources(Container {
kind: ContainerKind::Bag,
values: values.to_vec(),
}),
)
}
pub fn resource_sequence(&mut self, predicate: IRIRef, values: &[Resource]) -> &mut Self {
self.insert(
predicate,
ResourceObject::Resources(Container {
kind: ContainerKind::Seq,
values: values.to_vec(),
}),
)
}
pub fn resource_container(
&mut self,
predicate: IRIRef,
values: &[Resource],
kind: IRIRef,
) -> &mut Self {
self.insert(
predicate,
ResourceObject::Resources(Container {
kind: ContainerKind::Other(kind),
values: values.to_vec(),
}),
)
}
pub fn instance_of(&mut self, name: IRIRef) -> &mut Self {
self.insert(
rdf::a_type().clone(),
ResourceObject::Resource(Resource::named(name)),
)
}
fn insert(&mut self, predicate: IRIRef, object: ResourceObject) -> &mut Self {
if !self.predicates.contains_key(&predicate) {
self.predicates
.insert(predicate.clone(), RefCell::default());
}
let values = self.predicates.get(&predicate).unwrap();
values.borrow_mut().push(object);
self
}
}
impl ResourceObject {
pub fn is_container(&self) -> bool {
matches!(self, ResourceObject::Resources(_) | ResourceObject::Literals(_))
}
}
fn flatten(resource: &Resource, sts: &mut Vec<Statement>) {
let subject = &resource.subject;
for (predicate, objects) in &resource.predicates {
let objects = objects.borrow();
for object in objects.iter() {
if object.is_container() {
let kind = match object {
ResourceObject::Resources(rc) => &rc.kind,
ResourceObject::Literals(lc) => &lc.kind,
_ => unreachable!(),
};
let container = SubjectNode::blank();
sts.push(Statement::new(
subject.clone(),
predicate.clone(),
container.clone().into(),
));
sts.push(Statement::new(
container.clone(),
rdf::a_type().clone(),
match kind {
ContainerKind::Alt => rdf::alt(),
ContainerKind::Bag => rdf::bag(),
ContainerKind::Seq => rdf::seq(),
ContainerKind::Other(iri) => iri,
}
.into(),
));
match object {
ResourceObject::Resources(rc) => {
for (index, resource) in rc.values.iter().enumerate() {
flatten(resource, sts);
sts.push(Statement::new(
container.clone(),
rdf::member(index),
resource.subject.clone().into(),
));
}
}
ResourceObject::Literals(lc) => {
for (index, literal) in lc.values.iter().enumerate() {
sts.push(Statement::new(
container.clone(),
rdf::member(index),
literal.clone().into(),
));
}
}
_ => unreachable!(),
};
} else {
let statement = Statement::new(
subject.clone(),
predicate.clone(),
match object {
ResourceObject::Resource(resource) => {
flatten(resource, sts);
resource.subject.clone().into()
}
ResourceObject::Literal(literal) => literal.clone().into(),
_ => unreachable!(),
},
);
sts.push(statement);
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use rdftk_iri::IRI;
use std::str::FromStr;
fn contact(name: &str) -> IRIRef {
IRI::from_str(&format!(
"http://www.w3.org/2000/10/swap/pim/contact#{}",
name
))
.unwrap()
.into()
}
#[test]
fn test_wikipedia_example_01() {
let resource = Resource::named(
IRI::from_str("http://www.w3.org/People/EM/contact#me")
.unwrap()
.into(),
)
.literal(contact("fullName"), "Eric Miller".into())
.resource_named(
contact("mailbox"),
IRI::from_str("mailto:e.miller123(at)example")
.unwrap()
.into(),
)
.literal(contact("personalTitle"), "Dr.".into())
.instance_of(contact("Person"))
.to_owned();
let sts: Vec<Statement> = resource.into();
assert_eq!(sts.len(), 4);
for st in sts {
println!("{}", st);
}
}
}