use crate::model::{
NamedNode, Object, ObjectTerm, Predicate, RdfTerm, Subject, SubjectTerm, Triple,
};
use crate::query::algebra::{AlgebraTriplePattern, TermPattern};
use crate::OxirsError;
use std::fmt;
use std::sync::Arc;
#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct QuotedTriple {
inner: Arc<Triple>,
}
impl QuotedTriple {
pub fn new(triple: Triple) -> Self {
QuotedTriple {
inner: Arc::new(triple),
}
}
pub fn from_arc(triple: Arc<Triple>) -> Self {
QuotedTriple { inner: triple }
}
pub fn inner(&self) -> &Triple {
&self.inner
}
pub fn subject(&self) -> &Subject {
self.inner.subject()
}
pub fn predicate(&self) -> &Predicate {
self.inner.predicate()
}
pub fn object(&self) -> &Object {
self.inner.object()
}
pub fn as_ref(&self) -> QuotedTripleRef<'_> {
QuotedTripleRef { inner: &self.inner }
}
}
impl fmt::Display for QuotedTriple {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "<< {} >>", self.inner)
}
}
impl RdfTerm for QuotedTriple {
fn as_str(&self) -> &str {
"<<quoted-triple>>"
}
fn is_quoted_triple(&self) -> bool {
true
}
}
impl SubjectTerm for QuotedTriple {}
impl ObjectTerm for QuotedTriple {}
#[cfg(feature = "serde")]
impl serde::Serialize for QuotedTriple {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
self.inner.as_ref().serialize(serializer)
}
}
#[cfg(feature = "serde")]
impl<'de> serde::Deserialize<'de> for QuotedTriple {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
let triple = Triple::deserialize(deserializer)?;
Ok(QuotedTriple::new(triple))
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct QuotedTripleRef<'a> {
inner: &'a Triple,
}
impl<'a> QuotedTripleRef<'a> {
pub fn new(triple: &'a Triple) -> Self {
QuotedTripleRef { inner: triple }
}
pub fn inner(&self) -> &'a Triple {
self.inner
}
pub fn to_owned(&self) -> QuotedTriple {
QuotedTriple::new(self.inner.clone())
}
}
impl<'a> fmt::Display for QuotedTripleRef<'a> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "<< {} >>", self.inner)
}
}
impl<'a> RdfTerm for QuotedTripleRef<'a> {
fn as_str(&self) -> &str {
"<<quoted-triple>>"
}
fn is_quoted_triple(&self) -> bool {
true
}
}
pub struct Annotation {
pub statement: QuotedTriple,
pub property: NamedNode,
pub value: Object,
}
impl Annotation {
pub fn new(statement: Triple, property: NamedNode, value: Object) -> Self {
Annotation {
statement: QuotedTriple::new(statement),
property,
value,
}
}
pub fn to_triple(&self) -> Triple {
Triple::new(
Subject::QuotedTriple(Box::new(self.statement.clone())),
self.property.clone(),
self.value.clone(),
)
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum StarPattern {
Triple(AlgebraTriplePattern),
QuotedTriple {
subject: Box<StarPattern>,
predicate: TermPattern,
object: Box<StarPattern>,
},
Annotation {
statement: Box<StarPattern>,
property: TermPattern,
value: TermPattern,
},
}
impl StarPattern {
pub fn has_variables(&self) -> bool {
match self {
StarPattern::Triple(pattern) => {
matches!(pattern.subject, TermPattern::Variable(_))
|| matches!(pattern.predicate, TermPattern::Variable(_))
|| matches!(pattern.object, TermPattern::Variable(_))
}
StarPattern::QuotedTriple {
subject,
predicate: _,
object,
} => subject.has_variables() || object.has_variables(),
StarPattern::Annotation {
statement,
property: _,
value: _,
} => statement.has_variables(),
}
}
pub fn variables(&self) -> Vec<crate::model::Variable> {
let mut vars = Vec::new();
self.collect_variables(&mut vars);
vars
}
fn collect_variables(&self, _vars: &mut Vec<crate::model::Variable>) {
match self {
StarPattern::Triple(pattern) => {
if let TermPattern::Variable(ref v) = pattern.subject {
_vars.push(v.clone());
}
if let TermPattern::Variable(ref v) = pattern.predicate {
_vars.push(v.clone());
}
if let TermPattern::Variable(ref v) = pattern.object {
_vars.push(v.clone());
}
}
StarPattern::QuotedTriple {
subject,
predicate: _,
object,
} => {
subject.collect_variables(_vars);
object.collect_variables(_vars);
}
StarPattern::Annotation {
statement,
property: _,
value: _,
} => {
statement.collect_variables(_vars);
}
}
}
}
pub mod serialization {
use super::*;
pub mod turtle_star {
use super::*;
pub fn serialize_quoted_triple(qt: &QuotedTriple) -> String {
format!("<< {} {} {} >>", qt.subject(), qt.predicate(), qt.object())
}
pub fn parse_quoted_triple(input: &str) -> Result<QuotedTriple, OxirsError> {
let trimmed = input.trim();
if !trimmed.starts_with("<<") || !trimmed.ends_with(">>") {
return Err(OxirsError::Parse(
"Invalid quoted triple syntax".to_string(),
));
}
let _inner = &trimmed[2..trimmed.len() - 2].trim();
Err(OxirsError::Parse(
"Quoted triple parsing not yet implemented".to_string(),
))
}
}
pub mod sparql_star {
use super::*;
pub fn format_star_pattern(pattern: &StarPattern) -> String {
match pattern {
StarPattern::Triple(pattern) => pattern.to_string(),
StarPattern::QuotedTriple {
subject,
predicate: _,
object,
} => {
format!(
"<< {} {} {} >>",
format_star_pattern(subject),
"PREDICATE",
format_star_pattern(object)
)
}
StarPattern::Annotation {
statement,
property: _,
value: _,
} => {
format!(
"{} {} {}",
format_star_pattern(statement),
"PROPERTY",
"VALUE"
)
}
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::model::{Literal, NamedNode};
#[test]
fn test_quoted_triple() {
let subject = NamedNode::new("http://example.org/alice").expect("valid IRI");
let predicate = NamedNode::new("http://example.org/says").expect("valid IRI");
let object = Object::Literal(Literal::new("Hello"));
let triple = Triple::new(subject, predicate, object);
let quoted = QuotedTriple::new(triple.clone());
assert_eq!(quoted.inner(), &triple);
assert_eq!(
format!("{quoted}"),
"<< <http://example.org/alice> <http://example.org/says> \"Hello\" . >>"
);
}
#[test]
fn test_annotation() {
let subject = NamedNode::new("http://example.org/alice").expect("valid IRI");
let predicate = NamedNode::new("http://example.org/age").expect("valid IRI");
let object = Object::Literal(Literal::new_typed(
"30",
NamedNode::new("http://www.w3.org/2001/XMLSchema#integer").expect("valid IRI"),
));
let statement = Triple::new(subject, predicate, object);
let ann_property = NamedNode::new("http://example.org/confidence").expect("valid IRI");
let ann_value = Object::Literal(Literal::new_typed(
"0.9",
NamedNode::new("http://www.w3.org/2001/XMLSchema#double").expect("valid IRI"),
));
let annotation = Annotation::new(statement, ann_property, ann_value);
let ann_triple = annotation.to_triple();
assert!(matches!(ann_triple.subject(), Subject::QuotedTriple(_)));
}
}