use super::term::Term;
use serde::{Deserialize, Serialize};
use std::fmt;
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct Triple {
subject: Term,
predicate: Term,
object: Term,
}
impl Triple {
pub fn new(subject: Term, predicate: Term, object: Term) -> Self {
debug_assert!(
subject.is_iri() || subject.is_blank_node(),
"Subject must be an IRI or blank node"
);
debug_assert!(predicate.is_iri(), "Predicate must be an IRI");
Self {
subject,
predicate,
object,
}
}
#[inline]
pub fn new_unchecked(subject: Term, predicate: Term, object: Term) -> Self {
Self {
subject,
predicate,
object,
}
}
#[inline]
#[must_use]
pub fn subject(&self) -> &Term {
&self.subject
}
#[inline]
#[must_use]
pub fn predicate(&self) -> &Term {
&self.predicate
}
#[inline]
#[must_use]
pub fn object(&self) -> &Term {
&self.object
}
#[inline]
#[must_use]
pub fn into_parts(self) -> (Term, Term, Term) {
(self.subject, self.predicate, self.object)
}
#[inline]
#[must_use]
pub fn as_tuple(&self) -> (&Term, &Term, &Term) {
(&self.subject, &self.predicate, &self.object)
}
}
impl fmt::Display for Triple {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{} {} {} .", self.subject, self.predicate, self.object)
}
}
#[derive(Debug, Clone)]
pub struct TriplePattern {
pub subject: Option<Term>,
pub predicate: Option<Term>,
pub object: Option<Term>,
}
impl TriplePattern {
pub fn any() -> Self {
Self {
subject: None,
predicate: None,
object: None,
}
}
pub fn with_subject(subject: Term) -> Self {
Self {
subject: Some(subject),
predicate: None,
object: None,
}
}
pub fn with_predicate(predicate: Term) -> Self {
Self {
subject: None,
predicate: Some(predicate),
object: None,
}
}
pub fn with_object(object: Term) -> Self {
Self {
subject: None,
predicate: None,
object: Some(object),
}
}
pub fn matches(&self, triple: &Triple) -> bool {
if let Some(ref s) = self.subject
&& s != triple.subject()
{
return false;
}
if let Some(ref p) = self.predicate
&& p != triple.predicate()
{
return false;
}
if let Some(ref o) = self.object
&& o != triple.object()
{
return false;
}
true
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_triple_creation() {
let triple = Triple::new(
Term::iri("http://example.org/alix"),
Term::iri("http://xmlns.com/foaf/0.1/name"),
Term::literal("Alix"),
);
assert!(triple.subject().is_iri());
assert!(triple.predicate().is_iri());
assert!(triple.object().is_literal());
}
#[test]
fn test_triple_display() {
let triple = Triple::new(
Term::iri("http://example.org/alix"),
Term::iri("http://xmlns.com/foaf/0.1/name"),
Term::literal("Alix"),
);
let display = triple.to_string();
assert!(display.contains("<http://example.org/alix>"));
assert!(display.contains("<http://xmlns.com/foaf/0.1/name>"));
assert!(display.contains("\"Alix\""));
assert!(display.ends_with('.'));
}
#[test]
fn test_triple_pattern_matching() {
let triple = Triple::new(
Term::iri("http://example.org/alix"),
Term::iri("http://xmlns.com/foaf/0.1/name"),
Term::literal("Alix"),
);
assert!(TriplePattern::any().matches(&triple));
assert!(TriplePattern::with_subject(Term::iri("http://example.org/alix")).matches(&triple));
assert!(!TriplePattern::with_subject(Term::iri("http://example.org/gus")).matches(&triple));
assert!(
TriplePattern::with_predicate(Term::iri("http://xmlns.com/foaf/0.1/name"))
.matches(&triple)
);
}
}