spargebra/
term.rs

1//! Data structures for [RDF 1.1 Concepts](https://www.w3.org/TR/rdf11-concepts/) like IRI, literal or triples.
2
3pub use oxrdf::{BlankNode, Literal, NamedNode, NamedOrBlankNode, Term, Triple, Variable};
4use std::fmt;
5use std::fmt::Write;
6
7/// The union of [IRIs](https://www.w3.org/TR/rdf11-concepts/#dfn-iri), [literals](https://www.w3.org/TR/rdf11-concepts/#dfn-literal) and [triples](https://www.w3.org/TR/rdf11-concepts/#dfn-rdf-triple).
8///
9/// The default string formatter is returning an N-Triples, Turtle, and SPARQL compatible representation.
10#[derive(Eq, PartialEq, Debug, Clone, Hash)]
11pub enum GroundTerm {
12    NamedNode(NamedNode),
13    Literal(Literal),
14    #[cfg(feature = "sparql-12")]
15    Triple(Box<GroundTriple>),
16}
17
18impl fmt::Display for GroundTerm {
19    #[inline]
20    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
21        match self {
22            Self::NamedNode(node) => node.fmt(f),
23            Self::Literal(literal) => literal.fmt(f),
24            #[cfg(feature = "sparql-12")]
25            Self::Triple(triple) => write!(
26                f,
27                "<<( {} {} {} )>>",
28                triple.subject, triple.predicate, triple.object
29            ),
30        }
31    }
32}
33
34impl From<NamedNode> for GroundTerm {
35    #[inline]
36    fn from(node: NamedNode) -> Self {
37        Self::NamedNode(node)
38    }
39}
40
41impl From<Literal> for GroundTerm {
42    #[inline]
43    fn from(literal: Literal) -> Self {
44        Self::Literal(literal)
45    }
46}
47
48#[cfg(feature = "sparql-12")]
49impl From<GroundTriple> for GroundTerm {
50    #[inline]
51    fn from(triple: GroundTriple) -> Self {
52        Self::Triple(Box::new(triple))
53    }
54}
55
56impl TryFrom<Term> for GroundTerm {
57    type Error = ();
58
59    #[inline]
60    fn try_from(term: Term) -> Result<Self, Self::Error> {
61        match term {
62            Term::NamedNode(t) => Ok(t.into()),
63            Term::BlankNode(_) => Err(()),
64            Term::Literal(t) => Ok(t.into()),
65            #[cfg(feature = "sparql-12")]
66            Term::Triple(t) => Ok(GroundTriple::try_from(*t)?.into()),
67        }
68    }
69}
70
71impl From<GroundTerm> for Term {
72    #[inline]
73    fn from(term: GroundTerm) -> Self {
74        match term {
75            GroundTerm::NamedNode(t) => t.into(),
76            GroundTerm::Literal(l) => l.into(),
77            #[cfg(feature = "sparql-12")]
78            GroundTerm::Triple(t) => Triple::from(*t).into(),
79        }
80    }
81}
82
83/// A [RDF triple](https://www.w3.org/TR/rdf11-concepts/#dfn-rdf-triple) without blank nodes.
84///
85/// The default string formatter is returning a N-Quads representation.
86///
87/// ```
88/// use spargebra::term::{GroundTriple, NamedNode};
89///
90/// assert_eq!(
91///     "<http://example.com/s> <http://example.com/p> <http://example.com/o>",
92///     GroundTriple {
93///         subject: NamedNode::new("http://example.com/s")?.into(),
94///         predicate: NamedNode::new("http://example.com/p")?,
95///         object: NamedNode::new("http://example.com/o")?.into(),
96///     }
97///     .to_string()
98/// );
99/// # Result::<_,oxrdf::IriParseError>::Ok(())
100/// ```
101#[derive(Eq, PartialEq, Debug, Clone, Hash)]
102pub struct GroundTriple {
103    pub subject: NamedNode,
104    pub predicate: NamedNode,
105    pub object: GroundTerm,
106}
107
108impl fmt::Display for GroundTriple {
109    #[inline]
110    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
111        write!(f, "{} {} {}", self.subject, self.predicate, self.object)
112    }
113}
114
115impl TryFrom<Triple> for GroundTriple {
116    type Error = ();
117
118    #[inline]
119    fn try_from(triple: Triple) -> Result<Self, Self::Error> {
120        Ok(Self {
121            subject: if let NamedOrBlankNode::NamedNode(s) = triple.subject {
122                s
123            } else {
124                return Err(());
125            },
126            predicate: triple.predicate,
127            object: triple.object.try_into()?,
128        })
129    }
130}
131
132impl From<GroundTriple> for Triple {
133    #[inline]
134    fn from(triple: GroundTriple) -> Self {
135        Self {
136            subject: triple.subject.into(),
137            predicate: triple.predicate,
138            object: triple.object.into(),
139        }
140    }
141}
142
143/// A possible graph name.
144///
145/// 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).
146#[derive(Eq, PartialEq, Debug, Clone, Hash, Default)]
147pub enum GraphName {
148    NamedNode(NamedNode),
149    #[default]
150    DefaultGraph,
151}
152
153impl GraphName {
154    /// Formats using the [SPARQL S-Expression syntax](https://jena.apache.org/documentation/notes/sse.html).
155    pub(crate) fn fmt_sse(&self, f: &mut impl Write) -> fmt::Result {
156        match self {
157            Self::NamedNode(node) => write!(f, "{node}"),
158            Self::DefaultGraph => f.write_str("default"),
159        }
160    }
161}
162
163impl fmt::Display for GraphName {
164    #[inline]
165    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
166        match self {
167            Self::NamedNode(node) => node.fmt(f),
168            Self::DefaultGraph => f.write_str("DEFAULT"),
169        }
170    }
171}
172
173impl From<NamedNode> for GraphName {
174    #[inline]
175    fn from(node: NamedNode) -> Self {
176        Self::NamedNode(node)
177    }
178}
179
180impl TryFrom<GraphNamePattern> for GraphName {
181    type Error = ();
182
183    #[inline]
184    fn try_from(pattern: GraphNamePattern) -> Result<Self, Self::Error> {
185        match pattern {
186            GraphNamePattern::NamedNode(t) => Ok(t.into()),
187            GraphNamePattern::DefaultGraph => Ok(Self::DefaultGraph),
188            GraphNamePattern::Variable(_) => Err(()),
189        }
190    }
191}
192
193/// A [RDF triple](https://www.w3.org/TR/rdf11-concepts/#dfn-rdf-triple) in an [RDF dataset](https://www.w3.org/TR/rdf11-concepts/#dfn-rdf-dataset).
194///
195/// The default string formatter is returning a N-Quads representation.
196///
197/// ```
198/// use spargebra::term::{NamedNode, Quad};
199///
200/// assert_eq!(
201///     "<http://example.com/s> <http://example.com/p> <http://example.com/o> <http://example.com/g>",
202///     Quad {
203///         subject: NamedNode::new("http://example.com/s")?.into(),
204///         predicate: NamedNode::new("http://example.com/p")?,
205///         object: NamedNode::new("http://example.com/o")?.into(),
206///         graph_name: NamedNode::new("http://example.com/g")?.into(),
207///     }.to_string()
208/// );
209/// # Result::<_,oxrdf::IriParseError>::Ok(())
210/// ```
211#[derive(Eq, PartialEq, Debug, Clone, Hash)]
212pub struct Quad {
213    pub subject: NamedOrBlankNode,
214    pub predicate: NamedNode,
215    pub object: Term,
216    pub graph_name: GraphName,
217}
218
219impl Quad {
220    /// Formats using the [SPARQL S-Expression syntax](https://jena.apache.org/documentation/notes/sse.html).
221    pub(crate) fn fmt_sse(&self, f: &mut impl Write) -> fmt::Result {
222        if self.graph_name != GraphName::DefaultGraph {
223            f.write_str("(graph ")?;
224            self.graph_name.fmt_sse(f)?;
225            f.write_str(" (")?;
226        }
227        write!(
228            f,
229            "(triple {} {} {})",
230            self.subject, self.predicate, self.object
231        )?;
232        if self.graph_name != GraphName::DefaultGraph {
233            f.write_str("))")?;
234        }
235        Ok(())
236    }
237}
238
239impl fmt::Display for Quad {
240    #[inline]
241    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
242        if self.graph_name == GraphName::DefaultGraph {
243            write!(f, "{} {} {}", self.subject, self.predicate, self.object)
244        } else {
245            write!(
246                f,
247                "{} {} {} {}",
248                self.subject, self.predicate, self.object, self.graph_name
249            )
250        }
251    }
252}
253
254impl TryFrom<QuadPattern> for Quad {
255    type Error = ();
256
257    #[inline]
258    fn try_from(quad: QuadPattern) -> Result<Self, Self::Error> {
259        Ok(Self {
260            subject: quad.subject.try_into()?,
261            predicate: quad.predicate.try_into()?,
262            object: quad.object.try_into()?,
263            graph_name: quad.graph_name.try_into()?,
264        })
265    }
266}
267
268/// A [RDF triple](https://www.w3.org/TR/rdf11-concepts/#dfn-rdf-triple) in an [RDF dataset](https://www.w3.org/TR/rdf11-concepts/#dfn-rdf-dataset) without blank nodes.
269///
270/// The default string formatter is returning a N-Quads representation.
271///
272/// ```
273/// use spargebra::term::{NamedNode, GroundQuad};
274///
275/// assert_eq!(
276///     "<http://example.com/s> <http://example.com/p> <http://example.com/o> <http://example.com/g>",
277///     GroundQuad {
278///         subject: NamedNode::new("http://example.com/s")?.into(),
279///         predicate: NamedNode::new("http://example.com/p")?,
280///         object: NamedNode::new("http://example.com/o")?.into(),
281///         graph_name: NamedNode::new("http://example.com/g")?.into(),
282///     }.to_string()
283/// );
284/// # Result::<_,oxrdf::IriParseError>::Ok(())
285/// ```
286#[derive(Eq, PartialEq, Debug, Clone, Hash)]
287pub struct GroundQuad {
288    pub subject: NamedNode,
289    pub predicate: NamedNode,
290    pub object: GroundTerm,
291    pub graph_name: GraphName,
292}
293
294impl GroundQuad {
295    /// Formats using the [SPARQL S-Expression syntax](https://jena.apache.org/documentation/notes/sse.html).
296    pub(crate) fn fmt_sse(&self, f: &mut impl Write) -> fmt::Result {
297        if self.graph_name != GraphName::DefaultGraph {
298            f.write_str("(graph ")?;
299            self.graph_name.fmt_sse(f)?;
300            f.write_str(" (")?;
301        }
302        write!(
303            f,
304            "(triple {} {} {})",
305            self.subject, self.predicate, self.object
306        )?;
307        if self.graph_name != GraphName::DefaultGraph {
308            f.write_str("))")?;
309        }
310        Ok(())
311    }
312}
313
314impl fmt::Display for GroundQuad {
315    #[inline]
316    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
317        if self.graph_name == GraphName::DefaultGraph {
318            write!(f, "{} {} {}", self.subject, self.predicate, self.object)
319        } else {
320            write!(
321                f,
322                "{} {} {} {}",
323                self.subject, self.predicate, self.object, self.graph_name
324            )
325        }
326    }
327}
328
329impl TryFrom<Quad> for GroundQuad {
330    type Error = ();
331
332    #[inline]
333    fn try_from(quad: Quad) -> Result<Self, Self::Error> {
334        Ok(Self {
335            subject: if let NamedOrBlankNode::NamedNode(s) = quad.subject {
336                s
337            } else {
338                return Err(());
339            },
340            predicate: quad.predicate,
341            object: quad.object.try_into()?,
342            graph_name: quad.graph_name,
343        })
344    }
345}
346
347/// The union of [IRIs](https://www.w3.org/TR/rdf11-concepts/#dfn-iri) and [variables](https://www.w3.org/TR/sparql11-query/#sparqlQueryVariables).
348#[derive(Eq, PartialEq, Debug, Clone, Hash)]
349pub enum NamedNodePattern {
350    NamedNode(NamedNode),
351    Variable(Variable),
352}
353
354impl NamedNodePattern {
355    /// Formats using the [SPARQL S-Expression syntax](https://jena.apache.org/documentation/notes/sse.html).
356    pub(crate) fn fmt_sse(&self, f: &mut impl Write) -> fmt::Result {
357        match self {
358            Self::NamedNode(node) => write!(f, "{node}"),
359            Self::Variable(var) => write!(f, "{var}"),
360        }
361    }
362}
363
364impl fmt::Display for NamedNodePattern {
365    #[inline]
366    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
367        match self {
368            Self::NamedNode(node) => node.fmt(f),
369            Self::Variable(var) => var.fmt(f),
370        }
371    }
372}
373
374impl From<NamedNode> for NamedNodePattern {
375    #[inline]
376    fn from(node: NamedNode) -> Self {
377        Self::NamedNode(node)
378    }
379}
380
381impl From<Variable> for NamedNodePattern {
382    #[inline]
383    fn from(var: Variable) -> Self {
384        Self::Variable(var)
385    }
386}
387
388impl TryFrom<NamedNodePattern> for NamedNode {
389    type Error = ();
390
391    #[inline]
392    fn try_from(pattern: NamedNodePattern) -> Result<Self, Self::Error> {
393        match pattern {
394            NamedNodePattern::NamedNode(t) => Ok(t),
395            NamedNodePattern::Variable(_) => Err(()),
396        }
397    }
398}
399
400/// The union of [terms](https://www.w3.org/TR/rdf11-concepts/#dfn-rdf-term) and [variables](https://www.w3.org/TR/sparql11-query/#sparqlQueryVariables).
401#[derive(Eq, PartialEq, Debug, Clone, Hash)]
402pub enum TermPattern {
403    NamedNode(NamedNode),
404    BlankNode(BlankNode),
405    Literal(Literal),
406    #[cfg(feature = "sparql-12")]
407    Triple(Box<TriplePattern>),
408    Variable(Variable),
409}
410
411impl TermPattern {
412    /// Formats using the [SPARQL S-Expression syntax](https://jena.apache.org/documentation/notes/sse.html).
413    pub(crate) fn fmt_sse(&self, f: &mut impl Write) -> fmt::Result {
414        match self {
415            Self::NamedNode(term) => write!(f, "{term}"),
416            Self::BlankNode(term) => write!(f, "{term}"),
417            Self::Literal(term) => write!(f, "{term}"),
418            #[cfg(feature = "sparql-12")]
419            Self::Triple(triple) => triple.fmt_sse(f),
420            Self::Variable(var) => write!(f, "{var}"),
421        }
422    }
423}
424
425impl fmt::Display for TermPattern {
426    #[inline]
427    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
428        match self {
429            Self::NamedNode(term) => term.fmt(f),
430            Self::BlankNode(term) => term.fmt(f),
431            Self::Literal(term) => term.fmt(f),
432            #[cfg(feature = "sparql-12")]
433            Self::Triple(triple) => write!(f, "<<( {triple} )>>"),
434            Self::Variable(var) => var.fmt(f),
435        }
436    }
437}
438
439impl From<NamedNode> for TermPattern {
440    #[inline]
441    fn from(node: NamedNode) -> Self {
442        Self::NamedNode(node)
443    }
444}
445
446impl From<BlankNode> for TermPattern {
447    #[inline]
448    fn from(node: BlankNode) -> Self {
449        Self::BlankNode(node)
450    }
451}
452
453impl From<Literal> for TermPattern {
454    #[inline]
455    fn from(literal: Literal) -> Self {
456        Self::Literal(literal)
457    }
458}
459
460#[cfg(feature = "sparql-12")]
461impl From<TriplePattern> for TermPattern {
462    #[inline]
463    fn from(triple: TriplePattern) -> Self {
464        Self::Triple(Box::new(triple))
465    }
466}
467
468impl From<Variable> for TermPattern {
469    fn from(var: Variable) -> Self {
470        Self::Variable(var)
471    }
472}
473
474impl From<NamedOrBlankNode> for TermPattern {
475    #[inline]
476    fn from(subject: NamedOrBlankNode) -> Self {
477        match subject {
478            NamedOrBlankNode::NamedNode(node) => node.into(),
479            NamedOrBlankNode::BlankNode(node) => node.into(),
480        }
481    }
482}
483
484impl From<Term> for TermPattern {
485    #[inline]
486    fn from(term: Term) -> Self {
487        match term {
488            Term::NamedNode(node) => node.into(),
489            Term::BlankNode(node) => node.into(),
490            Term::Literal(literal) => literal.into(),
491            #[cfg(feature = "sparql-12")]
492            Term::Triple(t) => TriplePattern::from(*t).into(),
493        }
494    }
495}
496
497impl From<NamedNodePattern> for TermPattern {
498    #[inline]
499    fn from(element: NamedNodePattern) -> Self {
500        match element {
501            NamedNodePattern::NamedNode(node) => node.into(),
502            NamedNodePattern::Variable(var) => var.into(),
503        }
504    }
505}
506
507impl From<GroundTermPattern> for TermPattern {
508    #[inline]
509    fn from(element: GroundTermPattern) -> Self {
510        match element {
511            GroundTermPattern::NamedNode(node) => node.into(),
512            GroundTermPattern::Literal(literal) => literal.into(),
513            #[cfg(feature = "sparql-12")]
514            GroundTermPattern::Triple(t) => TriplePattern::from(*t).into(),
515            GroundTermPattern::Variable(variable) => variable.into(),
516        }
517    }
518}
519
520impl TryFrom<TermPattern> for NamedOrBlankNode {
521    type Error = ();
522
523    #[inline]
524    fn try_from(term: TermPattern) -> Result<Self, Self::Error> {
525        match term {
526            TermPattern::NamedNode(t) => Ok(t.into()),
527            TermPattern::BlankNode(t) => Ok(t.into()),
528            #[cfg(feature = "sparql-12")]
529            TermPattern::Triple(_) => Err(()),
530            TermPattern::Literal(_) | TermPattern::Variable(_) => Err(()),
531        }
532    }
533}
534
535impl TryFrom<TermPattern> for Term {
536    type Error = ();
537
538    #[inline]
539    fn try_from(pattern: TermPattern) -> Result<Self, Self::Error> {
540        match pattern {
541            TermPattern::NamedNode(t) => Ok(t.into()),
542            TermPattern::BlankNode(t) => Ok(t.into()),
543            TermPattern::Literal(t) => Ok(t.into()),
544            #[cfg(feature = "sparql-12")]
545            TermPattern::Triple(t) => Ok(Triple::try_from(*t)?.into()),
546            TermPattern::Variable(_) => Err(()),
547        }
548    }
549}
550/// 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.
551#[derive(Eq, PartialEq, Debug, Clone, Hash)]
552pub enum GroundTermPattern {
553    NamedNode(NamedNode),
554    Literal(Literal),
555    Variable(Variable),
556    #[cfg(feature = "sparql-12")]
557    Triple(Box<GroundTriplePattern>),
558}
559
560impl GroundTermPattern {
561    /// Formats using the [SPARQL S-Expression syntax](https://jena.apache.org/documentation/notes/sse.html).
562    pub(crate) fn fmt_sse(&self, f: &mut impl Write) -> fmt::Result {
563        match self {
564            Self::NamedNode(term) => write!(f, "{term}"),
565            Self::Literal(term) => write!(f, "{term}"),
566            Self::Variable(var) => write!(f, "{var}"),
567            #[cfg(feature = "sparql-12")]
568            Self::Triple(triple) => triple.fmt_sse(f),
569        }
570    }
571}
572
573impl fmt::Display for GroundTermPattern {
574    #[inline]
575    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
576        match self {
577            Self::NamedNode(term) => term.fmt(f),
578            Self::Literal(term) => term.fmt(f),
579            Self::Variable(var) => var.fmt(f),
580            #[cfg(feature = "sparql-12")]
581            Self::Triple(triple) => write!(f, "<<( {triple} )>>"),
582        }
583    }
584}
585
586impl From<NamedNode> for GroundTermPattern {
587    #[inline]
588    fn from(node: NamedNode) -> Self {
589        Self::NamedNode(node)
590    }
591}
592
593impl From<Literal> for GroundTermPattern {
594    #[inline]
595    fn from(literal: Literal) -> Self {
596        Self::Literal(literal)
597    }
598}
599
600#[cfg(feature = "sparql-12")]
601impl From<GroundTriplePattern> for GroundTermPattern {
602    #[inline]
603    fn from(triple: GroundTriplePattern) -> Self {
604        Self::Triple(Box::new(triple))
605    }
606}
607
608impl From<Variable> for GroundTermPattern {
609    #[inline]
610    fn from(var: Variable) -> Self {
611        Self::Variable(var)
612    }
613}
614
615impl From<GroundTerm> for GroundTermPattern {
616    #[inline]
617    fn from(term: GroundTerm) -> Self {
618        match term {
619            GroundTerm::NamedNode(node) => node.into(),
620            GroundTerm::Literal(literal) => literal.into(),
621            #[cfg(feature = "sparql-12")]
622            GroundTerm::Triple(triple) => GroundTriplePattern::from(*triple).into(),
623        }
624    }
625}
626
627impl From<NamedNodePattern> for GroundTermPattern {
628    #[inline]
629    fn from(element: NamedNodePattern) -> Self {
630        match element {
631            NamedNodePattern::NamedNode(node) => node.into(),
632            NamedNodePattern::Variable(var) => var.into(),
633        }
634    }
635}
636
637impl TryFrom<TermPattern> for GroundTermPattern {
638    type Error = ();
639
640    #[inline]
641    fn try_from(pattern: TermPattern) -> Result<Self, Self::Error> {
642        Ok(match pattern {
643            TermPattern::NamedNode(named_node) => named_node.into(),
644            TermPattern::BlankNode(_) => return Err(()),
645            TermPattern::Literal(literal) => literal.into(),
646            #[cfg(feature = "sparql-12")]
647            TermPattern::Triple(triple) => GroundTriplePattern::try_from(*triple)?.into(),
648            TermPattern::Variable(variable) => variable.into(),
649        })
650    }
651}
652
653/// 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).
654#[derive(Eq, PartialEq, Debug, Clone, Hash)]
655pub enum GraphNamePattern {
656    NamedNode(NamedNode),
657    DefaultGraph,
658    Variable(Variable),
659}
660
661impl GraphNamePattern {
662    /// Formats using the [SPARQL S-Expression syntax](https://jena.apache.org/documentation/notes/sse.html).
663    pub(crate) fn fmt_sse(&self, f: &mut impl Write) -> fmt::Result {
664        match self {
665            Self::NamedNode(node) => write!(f, "{node}"),
666            Self::DefaultGraph => f.write_str("default"),
667            Self::Variable(var) => write!(f, "{var}"),
668        }
669    }
670}
671
672impl fmt::Display for GraphNamePattern {
673    #[inline]
674    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
675        match self {
676            Self::NamedNode(node) => node.fmt(f),
677            Self::DefaultGraph => f.write_str("DEFAULT"),
678            Self::Variable(var) => var.fmt(f),
679        }
680    }
681}
682
683impl From<NamedNode> for GraphNamePattern {
684    #[inline]
685    fn from(node: NamedNode) -> Self {
686        Self::NamedNode(node)
687    }
688}
689
690impl From<Variable> for GraphNamePattern {
691    #[inline]
692    fn from(var: Variable) -> Self {
693        Self::Variable(var)
694    }
695}
696
697impl From<GraphName> for GraphNamePattern {
698    #[inline]
699    fn from(graph_name: GraphName) -> Self {
700        match graph_name {
701            GraphName::NamedNode(node) => node.into(),
702            GraphName::DefaultGraph => Self::DefaultGraph,
703        }
704    }
705}
706
707impl From<NamedNodePattern> for GraphNamePattern {
708    #[inline]
709    fn from(graph_name: NamedNodePattern) -> Self {
710        match graph_name {
711            NamedNodePattern::NamedNode(node) => node.into(),
712            NamedNodePattern::Variable(var) => var.into(),
713        }
714    }
715}
716
717/// A [triple pattern](https://www.w3.org/TR/sparql11-query/#defn_TriplePattern)
718#[derive(Eq, PartialEq, Debug, Clone, Hash)]
719pub struct TriplePattern {
720    pub subject: TermPattern,
721    pub predicate: NamedNodePattern,
722    pub object: TermPattern,
723}
724
725impl TriplePattern {
726    pub(crate) fn new(
727        subject: impl Into<TermPattern>,
728        predicate: impl Into<NamedNodePattern>,
729        object: impl Into<TermPattern>,
730    ) -> Self {
731        Self {
732            subject: subject.into(),
733            predicate: predicate.into(),
734            object: object.into(),
735        }
736    }
737
738    /// Formats using the [SPARQL S-Expression syntax](https://jena.apache.org/documentation/notes/sse.html).
739    pub(crate) fn fmt_sse(&self, f: &mut impl Write) -> fmt::Result {
740        f.write_str("(triple ")?;
741        self.subject.fmt_sse(f)?;
742        f.write_str(" ")?;
743        self.predicate.fmt_sse(f)?;
744        f.write_str(" ")?;
745        self.object.fmt_sse(f)?;
746        f.write_str(")")
747    }
748}
749
750impl fmt::Display for TriplePattern {
751    #[inline]
752    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
753        write!(f, "{} {} {}", self.subject, self.predicate, self.object)
754    }
755}
756
757impl From<Triple> for TriplePattern {
758    #[inline]
759    fn from(triple: Triple) -> Self {
760        Self {
761            subject: triple.subject.into(),
762            predicate: triple.predicate.into(),
763            object: triple.object.into(),
764        }
765    }
766}
767
768#[cfg(feature = "sparql-12")]
769impl From<GroundTriplePattern> for TriplePattern {
770    #[inline]
771    fn from(triple: GroundTriplePattern) -> Self {
772        Self {
773            subject: triple.subject.into(),
774            predicate: triple.predicate,
775            object: triple.object.into(),
776        }
777    }
778}
779
780impl TryFrom<TriplePattern> for Triple {
781    type Error = ();
782
783    #[inline]
784    fn try_from(triple: TriplePattern) -> Result<Self, Self::Error> {
785        Ok(Self {
786            subject: triple.subject.try_into()?,
787            predicate: triple.predicate.try_into()?,
788            object: triple.object.try_into()?,
789        })
790    }
791}
792
793/// A [triple pattern](https://www.w3.org/TR/sparql11-query/#defn_TriplePattern) without blank nodes.
794#[cfg(feature = "sparql-12")]
795#[derive(Eq, PartialEq, Debug, Clone, Hash)]
796pub struct GroundTriplePattern {
797    pub subject: GroundTermPattern,
798    pub predicate: NamedNodePattern,
799    pub object: GroundTermPattern,
800}
801
802#[cfg(feature = "sparql-12")]
803impl GroundTriplePattern {
804    /// Formats using the [SPARQL S-Expression syntax](https://jena.apache.org/documentation/notes/sse.html).
805    #[cfg(feature = "sparql-12")]
806    pub(crate) fn fmt_sse(&self, f: &mut impl Write) -> fmt::Result {
807        f.write_str("(triple ")?;
808        self.subject.fmt_sse(f)?;
809        f.write_str(" ")?;
810        self.predicate.fmt_sse(f)?;
811        f.write_str(" ")?;
812        self.object.fmt_sse(f)?;
813        f.write_str(")")
814    }
815}
816
817#[cfg(feature = "sparql-12")]
818impl fmt::Display for GroundTriplePattern {
819    #[inline]
820    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
821        write!(f, "{} {} {}", self.subject, self.predicate, self.object)
822    }
823}
824
825#[cfg(feature = "sparql-12")]
826impl From<GroundTriple> for GroundTriplePattern {
827    #[inline]
828    fn from(triple: GroundTriple) -> Self {
829        Self {
830            subject: triple.subject.into(),
831            predicate: triple.predicate.into(),
832            object: triple.object.into(),
833        }
834    }
835}
836
837#[cfg(feature = "sparql-12")]
838impl TryFrom<TriplePattern> for GroundTriplePattern {
839    type Error = ();
840
841    #[inline]
842    fn try_from(triple: TriplePattern) -> Result<Self, Self::Error> {
843        Ok(Self {
844            subject: triple.subject.try_into()?,
845            predicate: triple.predicate,
846            object: triple.object.try_into()?,
847        })
848    }
849}
850
851/// A [triple pattern](https://www.w3.org/TR/sparql11-query/#defn_TriplePattern) in a specific graph
852#[derive(Eq, PartialEq, Debug, Clone, Hash)]
853pub struct QuadPattern {
854    pub subject: TermPattern,
855    pub predicate: NamedNodePattern,
856    pub object: TermPattern,
857    pub graph_name: GraphNamePattern,
858}
859
860impl QuadPattern {
861    pub(crate) fn new(
862        subject: impl Into<TermPattern>,
863        predicate: impl Into<NamedNodePattern>,
864        object: impl Into<TermPattern>,
865        graph_name: impl Into<GraphNamePattern>,
866    ) -> Self {
867        Self {
868            subject: subject.into(),
869            predicate: predicate.into(),
870            object: object.into(),
871            graph_name: graph_name.into(),
872        }
873    }
874
875    /// Formats using the [SPARQL S-Expression syntax](https://jena.apache.org/documentation/notes/sse.html).
876    pub(crate) fn fmt_sse(&self, f: &mut impl Write) -> fmt::Result {
877        if self.graph_name != GraphNamePattern::DefaultGraph {
878            f.write_str("(graph ")?;
879            self.graph_name.fmt_sse(f)?;
880            f.write_str(" (")?;
881        }
882        f.write_str("(triple ")?;
883        self.subject.fmt_sse(f)?;
884        f.write_str(" ")?;
885        self.predicate.fmt_sse(f)?;
886        f.write_str(" ")?;
887        self.object.fmt_sse(f)?;
888        f.write_str(")")?;
889        if self.graph_name != GraphNamePattern::DefaultGraph {
890            f.write_str("))")?;
891        }
892        Ok(())
893    }
894}
895
896impl fmt::Display for QuadPattern {
897    #[inline]
898    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
899        if self.graph_name == GraphNamePattern::DefaultGraph {
900            write!(f, "{} {} {}", self.subject, self.predicate, self.object)
901        } else {
902            write!(
903                f,
904                "GRAPH {} {{ {} {} {} }}",
905                self.graph_name, self.subject, self.predicate, self.object
906            )
907        }
908    }
909}
910
911/// A [triple pattern](https://www.w3.org/TR/sparql11-query/#defn_TriplePattern) in a specific graph without blank nodes.
912#[derive(Eq, PartialEq, Debug, Clone, Hash)]
913pub struct GroundQuadPattern {
914    pub subject: GroundTermPattern,
915    pub predicate: NamedNodePattern,
916    pub object: GroundTermPattern,
917    pub graph_name: GraphNamePattern,
918}
919
920impl GroundQuadPattern {
921    /// Formats using the [SPARQL S-Expression syntax](https://jena.apache.org/documentation/notes/sse.html).
922    pub(crate) fn fmt_sse(&self, f: &mut impl Write) -> fmt::Result {
923        if self.graph_name != GraphNamePattern::DefaultGraph {
924            f.write_str("(graph ")?;
925            self.graph_name.fmt_sse(f)?;
926            f.write_str(" (")?;
927        }
928        f.write_str("(triple ")?;
929        self.subject.fmt_sse(f)?;
930        f.write_str(" ")?;
931        self.predicate.fmt_sse(f)?;
932        f.write_str(" ")?;
933        self.object.fmt_sse(f)?;
934        f.write_str(")")?;
935        if self.graph_name != GraphNamePattern::DefaultGraph {
936            f.write_str("))")?;
937        }
938        Ok(())
939    }
940}
941
942impl fmt::Display for GroundQuadPattern {
943    #[inline]
944    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
945        if self.graph_name == GraphNamePattern::DefaultGraph {
946            write!(f, "{} {} {}", self.subject, self.predicate, self.object)
947        } else {
948            write!(
949                f,
950                "GRAPH {} {{ {} {} {} }}",
951                self.graph_name, self.subject, self.predicate, self.object
952            )
953        }
954    }
955}
956
957impl TryFrom<QuadPattern> for GroundQuadPattern {
958    type Error = ();
959
960    #[inline]
961    fn try_from(pattern: QuadPattern) -> Result<Self, Self::Error> {
962        Ok(Self {
963            subject: pattern.subject.try_into()?,
964            predicate: pattern.predicate,
965            object: pattern.object.try_into()?,
966            graph_name: pattern.graph_name,
967        })
968    }
969}