//! Data structures for [RDF 1.1 Concepts](https://www.w3.org/TR/rdf11-concepts/) like IRI, literal or triples. use std::fmt; use std::fmt::Write; /// An RDF [IRI](https://www.w3.org/TR/rdf11-concepts/#dfn-iri). /// /// The default string formatter is returning an N-Triples, Turtle and SPARQL compatible representation. /// /// ``` /// use spargebra::term::NamedNode; /// /// assert_eq!( /// "", /// NamedNode { iri: "http://example.com/foo".into() }.to_string() /// ) /// ``` #[derive(Eq, PartialEq, Ord, PartialOrd, Debug, Clone, Hash)] pub struct NamedNode { /// The [IRI](https://www.w3.org/TR/rdf11-concepts/#dfn-iri) itself. pub iri: String, } impl fmt::Display for NamedNode { #[inline] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "<{}>", self.iri) } } /// An RDF [blank node](https://www.w3.org/TR/rdf11-concepts/#dfn-blank-node). /// /// /// The default string formatter is returning an N-Triples, Turtle and SPARQL compatible representation. /// /// ``` /// use spargebra::term::BlankNode; /// /// assert_eq!( /// "_:a1", /// BlankNode { id: "a1".into() }.to_string() /// ) /// ``` #[derive(Eq, PartialEq, Debug, Clone, Hash)] pub struct BlankNode { /// The [blank node identifier](https://www.w3.org/TR/rdf11-concepts/#dfn-blank-node-identifier). pub id: String, } impl fmt::Display for BlankNode { #[inline] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "_:{}", self.id) } } /// An RDF [literal](https://www.w3.org/TR/rdf11-concepts/#dfn-literal). /// /// The default string formatter is returning an N-Triples, Turtle and SPARQL compatible representation. /// /// The language tags should be lowercased [as suggested by the RDF specification](https://www.w3.org/TR/rdf11-concepts/#dfn-language-tagged-string). /// /// ``` /// use spargebra::term::NamedNode; /// use spargebra::term::Literal; /// /// assert_eq!( /// "\"foo\\nbar\"", /// Literal::Simple { value: "foo\nbar".into() }.to_string() /// ); /// /// assert_eq!( /// "\"1999-01-01\"^^", /// Literal::Typed { value: "1999-01-01".into(), datatype: NamedNode { iri: "http://www.w3.org/2001/XMLSchema#date".into() }}.to_string() /// ); /// /// assert_eq!( /// "\"foo\"@en", /// Literal::LanguageTaggedString { value: "foo".into(), language: "en".into() }.to_string() /// ); /// ``` #[derive(Eq, PartialEq, Debug, Clone, Hash)] pub enum Literal { /// A [simple literal](https://www.w3.org/TR/rdf11-concepts/#dfn-simple-literal) without datatype or language form. Simple { /// The [lexical form](https://www.w3.org/TR/rdf11-concepts/#dfn-lexical-form). value: String, }, /// A [language-tagged string](https://www.w3.org/TR/rdf11-concepts/#dfn-language-tagged-string) LanguageTaggedString { /// The [lexical form](https://www.w3.org/TR/rdf11-concepts/#dfn-lexical-form). value: String, /// The [language tag](https://www.w3.org/TR/rdf11-concepts/#dfn-language-tag). language: String, }, /// A literal with an explicit datatype Typed { /// The [lexical form](https://www.w3.org/TR/rdf11-concepts/#dfn-lexical-form). value: String, /// The [datatype IRI](https://www.w3.org/TR/rdf11-concepts/#dfn-datatype-iri). datatype: NamedNode, }, } impl fmt::Display for Literal { #[inline] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Literal::Simple { value } => print_quoted_str(value, f), Literal::LanguageTaggedString { value, language } => { print_quoted_str(value, f)?; write!(f, "@{}", language) } Literal::Typed { value, datatype } => { print_quoted_str(value, f)?; write!(f, "^^{}", datatype) } } } } /// The union of [IRIs](https://www.w3.org/TR/rdf11-concepts/#dfn-iri) and [blank nodes](https://www.w3.org/TR/rdf11-concepts/#dfn-blank-node). /// /// The default string formatter is returning an N-Triples, Turtle and SPARQL compatible representation. #[derive(Eq, PartialEq, Debug, Clone, Hash)] pub enum NamedOrBlankNode { NamedNode(NamedNode), BlankNode(BlankNode), } impl fmt::Display for NamedOrBlankNode { #[inline] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { NamedOrBlankNode::NamedNode(node) => node.fmt(f), NamedOrBlankNode::BlankNode(node) => node.fmt(f), } } } impl From for NamedOrBlankNode { #[inline] fn from(node: NamedNode) -> Self { Self::NamedNode(node) } } impl From for NamedOrBlankNode { #[inline] fn from(node: BlankNode) -> Self { Self::BlankNode(node) } } /// An RDF [term](https://www.w3.org/TR/rdf11-concepts/#dfn-rdf-term). /// /// It is the union of [IRIs](https://www.w3.org/TR/rdf11-concepts/#dfn-iri), [blank nodes](https://www.w3.org/TR/rdf11-concepts/#dfn-blank-node) and [literals](https://www.w3.org/TR/rdf11-concepts/#dfn-literal). /// /// The default string formatter is returning an N-Triples, Turtle and SPARQL compatible representation. #[derive(Eq, PartialEq, Debug, Clone, Hash)] pub enum Term { NamedNode(NamedNode), BlankNode(BlankNode), Literal(Literal), } impl fmt::Display for Term { #[inline] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Term::NamedNode(node) => node.fmt(f), Term::BlankNode(node) => node.fmt(f), Term::Literal(literal) => literal.fmt(f), } } } impl From for Term { #[inline] fn from(node: NamedNode) -> Self { Self::NamedNode(node) } } impl From for Term { #[inline] fn from(node: BlankNode) -> Self { Self::BlankNode(node) } } impl From for Term { #[inline] fn from(literal: Literal) -> Self { Self::Literal(literal) } } impl From for Term { #[inline] fn from(resource: NamedOrBlankNode) -> Self { match resource { NamedOrBlankNode::NamedNode(node) => Self::NamedNode(node), NamedOrBlankNode::BlankNode(node) => Self::BlankNode(node), } } } /// The union of [IRIs](https://www.w3.org/TR/rdf11-concepts/#dfn-iri) and [literals](https://www.w3.org/TR/rdf11-concepts/#dfn-literal). /// /// The default string formatter is returning an N-Triples, Turtle and SPARQL compatible representation. #[derive(Eq, PartialEq, Debug, Clone, Hash)] pub enum GroundTerm { NamedNode(NamedNode), Literal(Literal), } impl fmt::Display for GroundTerm { #[inline] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { GroundTerm::NamedNode(node) => node.fmt(f), GroundTerm::Literal(literal) => literal.fmt(f), } } } impl From for GroundTerm { #[inline] fn from(node: NamedNode) -> Self { Self::NamedNode(node) } } impl From for GroundTerm { #[inline] fn from(literal: Literal) -> Self { Self::Literal(literal) } } /// A possible graph name. /// /// It is the union of [IRIs](https://www.w3.org/TR/rdf11-concepts/#dfn-iri) and the [default graph name](https://www.w3.org/TR/rdf11-concepts/#dfn-default-graph). #[derive(Eq, PartialEq, Debug, Clone, Hash)] pub enum GraphName { NamedNode(NamedNode), DefaultGraph, } impl fmt::Display for GraphName { #[inline] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { GraphName::NamedNode(node) => node.fmt(f), GraphName::DefaultGraph => write!(f, "DEFAULT"), } } } impl From for GraphName { #[inline] fn from(node: NamedNode) -> Self { Self::NamedNode(node) } } /// A [RDF triple](https://www.w3.org/TR/rdf11-concepts/#dfn-rdf-triple) in a [RDF dataset](https://www.w3.org/TR/rdf11-concepts/#dfn-rdf-dataset). /// /// The default string formatter is returning a N-Quads representation. /// /// ``` /// use spargebra::term::NamedNode; /// use spargebra::term::Quad; /// /// assert_eq!( /// " .", /// Quad { /// subject: NamedNode { iri: "http://example.com/foo".into() }.into(), /// predicate: NamedNode { iri: "http://schema.org/sameAs".into() }, /// object: NamedNode { iri: "http://example.com/foo".into() }.into(), /// graph_name: NamedNode { iri: "http://example.com/".into() }.into(), /// }.to_string() /// ) /// ``` #[derive(Eq, PartialEq, Debug, Clone, Hash)] pub struct Quad { pub subject: NamedOrBlankNode, pub predicate: NamedNode, pub object: Term, pub graph_name: GraphName, } impl fmt::Display for Quad { #[inline] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { if self.graph_name == GraphName::DefaultGraph { write!(f, "{} {} {} .", self.subject, self.predicate, self.object) } else { write!( f, "{} {} {} {} .", self.subject, self.predicate, self.object, self.graph_name ) } } } /// A [RDF triple](https://www.w3.org/TR/rdf11-concepts/#dfn-rdf-triple) in a [RDF dataset](https://www.w3.org/TR/rdf11-concepts/#dfn-rdf-dataset) without blank nodes. /// /// The default string formatter is returning a N-Quads representation. /// /// ``` /// use spargebra::term::NamedNode; /// use spargebra::term::GroundQuad; /// /// assert_eq!( /// " .", /// GroundQuad { /// subject: NamedNode { iri: "http://example.com/foo".into() }.into(), /// predicate: NamedNode { iri: "http://schema.org/sameAs".into() }, /// object: NamedNode { iri: "http://example.com/foo".into() }.into(), /// graph_name: NamedNode { iri: "http://example.com/".into() }.into(), /// }.to_string() /// ) /// ``` #[derive(Eq, PartialEq, Debug, Clone, Hash)] pub struct GroundQuad { pub subject: NamedNode, pub predicate: NamedNode, pub object: GroundTerm, pub graph_name: GraphName, } impl fmt::Display for GroundQuad { #[inline] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { if self.graph_name == GraphName::DefaultGraph { write!(f, "{} {} {} .", self.subject, self.predicate, self.object) } else { write!( f, "{} {} {} {} .", self.subject, self.predicate, self.object, self.graph_name ) } } } /// A [SPARQL query variable](https://www.w3.org/TR/sparql11-query/#sparqlQueryVariables). /// /// ``` /// use spargebra::term::Variable; /// /// assert_eq!( /// "?foo", /// Variable { name: "foo".into() }.to_string() /// ); /// ``` #[derive(Eq, PartialEq, Ord, PartialOrd, Debug, Clone, Hash)] pub struct Variable { pub name: String, } impl fmt::Display for Variable { #[inline] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "?{}", self.name) } } /// The union of [IRIs](https://www.w3.org/TR/rdf11-concepts/#dfn-iri) and [variables](https://www.w3.org/TR/sparql11-query/#sparqlQueryVariables). #[derive(Eq, PartialEq, Debug, Clone, Hash)] pub enum NamedNodePattern { NamedNode(NamedNode), Variable(Variable), } impl fmt::Display for NamedNodePattern { #[inline] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { NamedNodePattern::NamedNode(node) => node.fmt(f), NamedNodePattern::Variable(var) => var.fmt(f), } } } impl From for NamedNodePattern { #[inline] fn from(node: NamedNode) -> Self { Self::NamedNode(node) } } impl From for NamedNodePattern { #[inline] fn from(var: Variable) -> Self { Self::Variable(var) } } /// The union of [terms](https://www.w3.org/TR/rdf11-concepts/#dfn-rdf-term) and [variables](https://www.w3.org/TR/sparql11-query/#sparqlQueryVariables). #[derive(Eq, PartialEq, Debug, Clone, Hash)] pub enum TermPattern { NamedNode(NamedNode), BlankNode(BlankNode), Literal(Literal), Variable(Variable), } impl fmt::Display for TermPattern { #[inline] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { TermPattern::NamedNode(term) => term.fmt(f), TermPattern::BlankNode(term) => term.fmt(f), TermPattern::Literal(term) => term.fmt(f), TermPattern::Variable(var) => var.fmt(f), } } } impl From for TermPattern { #[inline] fn from(node: NamedNode) -> Self { Self::NamedNode(node) } } impl From for TermPattern { #[inline] fn from(node: BlankNode) -> Self { Self::BlankNode(node) } } impl From for TermPattern { #[inline] fn from(literal: Literal) -> Self { Self::Literal(literal) } } impl From for TermPattern { fn from(var: Variable) -> Self { Self::Variable(var) } } impl From for TermPattern { #[inline] fn from(term: Term) -> Self { match term { Term::NamedNode(node) => Self::NamedNode(node), Term::BlankNode(node) => Self::BlankNode(node), Term::Literal(literal) => Self::Literal(literal), } } } impl From for TermPattern { #[inline] fn from(element: NamedNodePattern) -> Self { match element { NamedNodePattern::NamedNode(node) => Self::NamedNode(node), NamedNodePattern::Variable(var) => Self::Variable(var), } } } /// The union of [terms](https://www.w3.org/TR/rdf11-concepts/#dfn-rdf-term) and [variables](https://www.w3.org/TR/sparql11-query/#sparqlQueryVariables) without blank nodes. #[derive(Eq, PartialEq, Debug, Clone, Hash)] pub enum GroundTermPattern { NamedNode(NamedNode), Literal(Literal), Variable(Variable), } impl fmt::Display for GroundTermPattern { #[inline] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { GroundTermPattern::NamedNode(term) => term.fmt(f), GroundTermPattern::Literal(term) => term.fmt(f), GroundTermPattern::Variable(var) => var.fmt(f), } } } impl From for GroundTermPattern { #[inline] fn from(node: NamedNode) -> Self { Self::NamedNode(node) } } impl From for GroundTermPattern { #[inline] fn from(literal: Literal) -> Self { Self::Literal(literal) } } impl From for GroundTermPattern { #[inline] fn from(var: Variable) -> Self { Self::Variable(var) } } impl From for GroundTermPattern { #[inline] fn from(term: GroundTerm) -> Self { match term { GroundTerm::NamedNode(node) => Self::NamedNode(node), GroundTerm::Literal(literal) => Self::Literal(literal), } } } impl From for GroundTermPattern { #[inline] fn from(element: NamedNodePattern) -> Self { match element { NamedNodePattern::NamedNode(node) => Self::NamedNode(node), NamedNodePattern::Variable(var) => Self::Variable(var), } } } /// The union of [IRIs](https://www.w3.org/TR/rdf11-concepts/#dfn-iri), [default graph name](https://www.w3.org/TR/rdf11-concepts/#dfn-default-graph) and [variables](https://www.w3.org/TR/sparql11-query/#sparqlQueryVariables). #[derive(Eq, PartialEq, Debug, Clone, Hash)] pub enum GraphNamePattern { NamedNode(NamedNode), DefaultGraph, Variable(Variable), } impl fmt::Display for GraphNamePattern { #[inline] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { GraphNamePattern::NamedNode(node) => node.fmt(f), GraphNamePattern::DefaultGraph => f.write_str("DEFAULT"), GraphNamePattern::Variable(var) => var.fmt(f), } } } impl From for GraphNamePattern { #[inline] fn from(node: NamedNode) -> Self { Self::NamedNode(node) } } impl From for GraphNamePattern { #[inline] fn from(var: Variable) -> Self { Self::Variable(var) } } impl From for GraphNamePattern { #[inline] fn from(graph_name: GraphName) -> Self { match graph_name { GraphName::NamedNode(node) => Self::NamedNode(node), GraphName::DefaultGraph => Self::DefaultGraph, } } } impl From for GraphNamePattern { #[inline] fn from(graph_name: NamedNodePattern) -> Self { match graph_name { NamedNodePattern::NamedNode(node) => Self::NamedNode(node), NamedNodePattern::Variable(var) => Self::Variable(var), } } } /// A [triple pattern](https://www.w3.org/TR/sparql11-query/#defn_TriplePattern) #[derive(Eq, PartialEq, Debug, Clone, Hash)] pub struct TriplePattern { pub subject: TermPattern, pub predicate: NamedNodePattern, pub object: TermPattern, } impl TriplePattern { pub(crate) fn new( subject: impl Into, predicate: impl Into, object: impl Into, ) -> Self { Self { subject: subject.into(), predicate: predicate.into(), object: object.into(), } } } impl fmt::Display for TriplePattern { #[inline] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!( f, "(triple {} {} {})", self.subject, self.predicate, self.object ) } } /// A [triple pattern](https://www.w3.org/TR/sparql11-query/#defn_TriplePattern) in a specific graph #[derive(Eq, PartialEq, Debug, Clone, Hash)] pub struct QuadPattern { pub subject: TermPattern, pub predicate: NamedNodePattern, pub object: TermPattern, pub graph_name: GraphNamePattern, } impl QuadPattern { pub(crate) fn new( subject: impl Into, predicate: impl Into, object: impl Into, graph_name: impl Into, ) -> Self { Self { subject: subject.into(), predicate: predicate.into(), object: object.into(), graph_name: graph_name.into(), } } } impl fmt::Display for QuadPattern { #[inline] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { if self.graph_name == GraphNamePattern::DefaultGraph { write!( f, "(triple {} {} {})", self.subject, self.predicate, self.object ) } else { write!( f, "(graph {} (triple {} {} {}))", self.graph_name, self.subject, self.predicate, self.object ) } } } /// A [triple pattern](https://www.w3.org/TR/sparql11-query/#defn_TriplePattern) in a specific graph without blank nodes #[derive(Eq, PartialEq, Debug, Clone, Hash)] pub struct GroundQuadPattern { pub subject: GroundTermPattern, pub predicate: NamedNodePattern, pub object: GroundTermPattern, pub graph_name: GraphNamePattern, } impl fmt::Display for GroundQuadPattern { #[inline] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { if self.graph_name == GraphNamePattern::DefaultGraph { write!( f, "(triple {} {} {})", self.subject, self.predicate, self.object ) } else { write!( f, "(graph {} (triple {} {} {}))", self.graph_name, self.subject, self.predicate, self.object ) } } } #[inline] pub(crate) fn print_quoted_str(string: &str, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.write_char('"')?; for c in string.chars() { match c { '\n' => f.write_str("\\n"), '\r' => f.write_str("\\r"), '"' => f.write_str("\\\""), '\\' => f.write_str("\\\\"), c => f.write_char(c), }?; } f.write_char('"') }