spareval/
dataset.rs

1use oxrdf::{
2    BlankNode, Dataset, GraphNameRef, Literal, NamedNode, QuadRef, SubjectRef, Term, TermRef,
3};
4#[cfg(feature = "rdf-star")]
5use oxrdf::{Subject, Triple};
6use oxsdatatypes::{Boolean, DateTime, Decimal, Double, Float, Integer};
7#[cfg(feature = "sep-0002")]
8use oxsdatatypes::{Date, DayTimeDuration, Duration, Time, YearMonthDuration};
9#[cfg(feature = "calendar-ext")]
10use oxsdatatypes::{GDay, GMonth, GMonthDay, GYear, GYearMonth};
11use rustc_hash::FxHashSet;
12use std::convert::Infallible;
13use std::error::Error;
14use std::hash::{Hash, Hasher};
15use std::iter::empty;
16use std::mem::discriminant;
17
18/// A [RDF dataset](https://www.w3.org/TR/sparql11-query/#rdfDataset) that can be queried using SPARQL
19pub trait QueryableDataset: Sized + 'static {
20    /// Internal representation of an RDF term
21    ///
22    /// Can be just an integer that indexes into a dictionary...
23    ///
24    /// Equality here is the RDF term equality (SPARQL `sameTerm` function)
25    type InternalTerm: Clone + Eq + Hash;
26
27    /// Error returned by the dataset.
28    type Error: Error + Send + Sync;
29
30    /// Fetches quads according to a pattern
31    ///
32    /// For `graph_name`, `Some(None)` encodes the default graph and `Some(Some(_))` a named graph
33    fn internal_quads_for_pattern(
34        &self,
35        subject: Option<&Self::InternalTerm>,
36        predicate: Option<&Self::InternalTerm>,
37        object: Option<&Self::InternalTerm>,
38        graph_name: Option<Option<&Self::InternalTerm>>,
39    ) -> Box<dyn Iterator<Item = Result<InternalQuad<Self>, Self::Error>>>; // TODO: consider `impl`
40
41    /// Fetches the list of dataset named graphs
42    fn internal_named_graphs(
43        &self,
44    ) -> Box<dyn Iterator<Item = Result<Self::InternalTerm, Self::Error>>> {
45        // TODO: consider `impl`
46        let mut error = None;
47        let graph_names = self
48            .internal_quads_for_pattern(None, None, None, None)
49            .filter_map(|r| match r {
50                Ok(r) => Some(r.graph_name?),
51                Err(e) => {
52                    error = Some(e);
53                    None
54                }
55            })
56            .collect::<FxHashSet<_>>();
57
58        Box::new(
59            error
60                .map(Err)
61                .into_iter()
62                .chain(graph_names.into_iter().map(Ok)),
63        )
64    }
65
66    /// Returns if the dataset contains a given named graph
67    fn contains_internal_graph_name(
68        &self,
69        graph_name: &Self::InternalTerm,
70    ) -> Result<bool, Self::Error> {
71        Ok(self
72            .internal_quads_for_pattern(None, None, None, Some(Some(graph_name)))
73            .next()
74            .transpose()?
75            .is_some())
76    }
77
78    /// Builds an internal term from the [`Term`] struct
79    fn internalize_term(&self, term: Term) -> Result<Self::InternalTerm, Self::Error>;
80
81    /// Builds a [`Term`] from an internal term
82    fn externalize_term(&self, term: Self::InternalTerm) -> Result<Term, Self::Error>;
83
84    // Optional methods that can be overridden for better performances
85
86    /// Builds an [`ExpressionTerm`] from an internal term
87    fn externalize_expression_term(
88        &self,
89        term: Self::InternalTerm,
90    ) -> Result<ExpressionTerm, Self::Error> {
91        Ok(self.externalize_term(term)?.into())
92    }
93
94    /// Builds an internal term from an [`ExpressionTerm`]
95    fn internalize_expression_term(
96        &self,
97        term: ExpressionTerm,
98    ) -> Result<Self::InternalTerm, Self::Error> {
99        self.internalize_term(term.into())
100    }
101
102    /// Computes the term [Effective boolean value](https://www.w3.org/TR/sparql11-query/#ebv)
103    fn internal_term_effective_boolean_value(
104        &self,
105        term: Self::InternalTerm,
106    ) -> Result<Option<bool>, Self::Error> {
107        Ok(self
108            .externalize_expression_term(term)?
109            .effective_boolean_value())
110    }
111}
112
113impl QueryableDataset for Dataset {
114    type InternalTerm = Term;
115    type Error = Infallible;
116
117    fn internal_quads_for_pattern(
118        &self,
119        subject: Option<&Term>,
120        predicate: Option<&Term>,
121        object: Option<&Term>,
122        graph_name: Option<Option<&Term>>,
123    ) -> Box<dyn Iterator<Item = Result<InternalQuad<Self>, Infallible>>> {
124        // Awful implementation, please don't take it as an example
125
126        #[allow(clippy::unnecessary_wraps)]
127        fn quad_to_result(quad: QuadRef<'_>) -> Result<InternalQuad<Dataset>, Infallible> {
128            Ok(InternalQuad {
129                subject: quad.subject.into(),
130                predicate: quad.predicate.into(),
131                object: quad.object.into_owned(),
132                graph_name: match quad.graph_name {
133                    GraphNameRef::NamedNode(g) => Some(g.into()),
134                    GraphNameRef::BlankNode(g) => Some(g.into()),
135                    GraphNameRef::DefaultGraph => None,
136                },
137            })
138        }
139
140        let subject = if let Some(subject) = subject {
141            Some(match TermRef::from(subject) {
142                TermRef::NamedNode(s) => SubjectRef::from(s),
143                TermRef::BlankNode(s) => s.into(),
144                TermRef::Literal(_) => return Box::new(empty()),
145                #[cfg(feature = "rdf-star")]
146                TermRef::Triple(s) => s.into(),
147            })
148        } else {
149            None
150        };
151        let predicate = if let Some(predicate) = predicate {
152            if let TermRef::NamedNode(p) = TermRef::from(predicate) {
153                Some(p)
154            } else {
155                return Box::new(empty());
156            }
157        } else {
158            None
159        };
160        let object = object.map(TermRef::from);
161        let graph_name = if let Some(graph_name) = graph_name {
162            Some(if let Some(graph_name) = graph_name {
163                match TermRef::from(graph_name) {
164                    TermRef::NamedNode(s) => s.into(),
165                    TermRef::BlankNode(s) => s.into(),
166                    TermRef::Literal(_) => return Box::new(empty()),
167                    #[cfg(feature = "rdf-star")]
168                    TermRef::Triple(_) => return Box::new(empty()),
169                }
170            } else {
171                GraphNameRef::DefaultGraph
172            })
173        } else {
174            None
175        };
176        let quads: Vec<_> = if let Some(subject) = subject {
177            self.quads_for_subject(subject)
178                .filter(|q| {
179                    predicate.map_or(true, |t| t == q.predicate)
180                        && object.map_or(true, |t| t == q.object)
181                        && graph_name
182                            .map_or_else(|| !q.graph_name.is_default_graph(), |t| t == q.graph_name)
183                })
184                .map(quad_to_result)
185                .collect()
186        } else if let Some(object) = object {
187            self.quads_for_object(object)
188                .filter(|q| {
189                    predicate.map_or(true, |t| t == q.predicate)
190                        && graph_name
191                            .map_or_else(|| !q.graph_name.is_default_graph(), |t| t == q.graph_name)
192                })
193                .map(quad_to_result)
194                .collect()
195        } else if let Some(predicate) = predicate {
196            self.quads_for_predicate(predicate)
197                .filter(|q| {
198                    graph_name
199                        .map_or_else(|| !q.graph_name.is_default_graph(), |t| t == q.graph_name)
200                })
201                .map(quad_to_result)
202                .collect()
203        } else if let Some(graph_name) = graph_name {
204            self.quads_for_graph_name(graph_name)
205                .map(quad_to_result)
206                .collect()
207        } else {
208            self.iter()
209                .filter(|q| !q.graph_name.is_default_graph())
210                .map(quad_to_result)
211                .collect()
212        };
213        Box::new(quads.into_iter())
214    }
215
216    fn internalize_term(&self, term: Term) -> Result<Term, Infallible> {
217        Ok(term)
218    }
219
220    fn externalize_term(&self, term: Term) -> Result<Term, Infallible> {
221        Ok(term)
222    }
223}
224
225pub struct InternalQuad<D: QueryableDataset> {
226    pub subject: D::InternalTerm,
227    pub predicate: D::InternalTerm,
228    pub object: D::InternalTerm,
229    /// `None` if the quad is in the default graph
230    pub graph_name: Option<D::InternalTerm>,
231}
232
233/// A term as understood by the expression evaluator
234#[derive(Clone)]
235pub enum ExpressionTerm {
236    NamedNode(NamedNode),
237    BlankNode(BlankNode),
238    StringLiteral(String),
239    LangStringLiteral {
240        value: String,
241        language: String,
242    },
243    BooleanLiteral(Boolean),
244    IntegerLiteral(Integer),
245    DecimalLiteral(Decimal),
246    FloatLiteral(Float),
247    DoubleLiteral(Double),
248    DateTimeLiteral(DateTime),
249    #[cfg(feature = "sep-0002")]
250    DateLiteral(Date),
251    #[cfg(feature = "sep-0002")]
252    TimeLiteral(Time),
253    #[cfg(feature = "calendar-ext")]
254    GYearLiteral(GYear),
255    #[cfg(feature = "calendar-ext")]
256    GYearMonthLiteral(GYearMonth),
257    #[cfg(feature = "calendar-ext")]
258    GMonthLiteral(GMonth),
259    #[cfg(feature = "calendar-ext")]
260    GMonthDayLiteral(GMonthDay),
261    #[cfg(feature = "calendar-ext")]
262    GDayLiteral(GDay),
263    #[cfg(feature = "sep-0002")]
264    DurationLiteral(Duration),
265    #[cfg(feature = "sep-0002")]
266    YearMonthDurationLiteral(YearMonthDuration),
267    #[cfg(feature = "sep-0002")]
268    DayTimeDurationLiteral(DayTimeDuration),
269    OtherTypedLiteral {
270        value: String,
271        datatype: NamedNode,
272    },
273    #[cfg(feature = "rdf-star")]
274    Triple(Box<ExpressionTriple>),
275}
276
277impl PartialEq for ExpressionTerm {
278    #[inline]
279    fn eq(&self, other: &Self) -> bool {
280        discriminant(self) == discriminant(other)
281            && match (self, other) {
282                (Self::NamedNode(l), Self::NamedNode(r)) => l == r,
283                (Self::BlankNode(l), Self::BlankNode(r)) => l == r,
284                (Self::StringLiteral(l), Self::StringLiteral(r)) => l == r,
285                (
286                    Self::LangStringLiteral {
287                        value: lv,
288                        language: ll,
289                    },
290                    Self::LangStringLiteral {
291                        value: rv,
292                        language: rl,
293                    },
294                ) => lv == rv && ll == rl,
295                (Self::BooleanLiteral(l), Self::BooleanLiteral(r)) => l == r,
296                (Self::IntegerLiteral(l), Self::IntegerLiteral(r)) => l == r,
297                (Self::DecimalLiteral(l), Self::DecimalLiteral(r)) => l == r,
298                (Self::FloatLiteral(l), Self::FloatLiteral(r)) => l.is_identical_with(*r),
299                (Self::DoubleLiteral(l), Self::DoubleLiteral(r)) => l.is_identical_with(*r),
300                (Self::DateTimeLiteral(l), Self::DateTimeLiteral(r)) => l == r,
301                #[cfg(feature = "sep-0002")]
302                (Self::DateLiteral(l), Self::DateLiteral(r)) => l == r,
303                #[cfg(feature = "sep-0002")]
304                (Self::TimeLiteral(l), Self::TimeLiteral(r)) => l == r,
305                #[cfg(feature = "calendar-ext")]
306                (Self::GYearMonthLiteral(l), Self::GYearMonthLiteral(r)) => l == r,
307                #[cfg(feature = "calendar-ext")]
308                (Self::GYearLiteral(l), Self::GYearLiteral(r)) => l == r,
309                #[cfg(feature = "calendar-ext")]
310                (Self::GMonthLiteral(l), Self::GMonthLiteral(r)) => l == r,
311                #[cfg(feature = "calendar-ext")]
312                (Self::GMonthDayLiteral(l), Self::GMonthDayLiteral(r)) => l == r,
313                #[cfg(feature = "calendar-ext")]
314                (Self::GDayLiteral(l), Self::GDayLiteral(r)) => l == r,
315                #[cfg(feature = "sep-0002")]
316                (Self::DurationLiteral(l), Self::DurationLiteral(r)) => l == r,
317                #[cfg(feature = "sep-0002")]
318                (Self::YearMonthDurationLiteral(l), Self::YearMonthDurationLiteral(r)) => l == r,
319                #[cfg(feature = "sep-0002")]
320                (Self::DayTimeDurationLiteral(l), Self::DayTimeDurationLiteral(r)) => l == r,
321                (
322                    Self::OtherTypedLiteral {
323                        value: lv,
324                        datatype: ld,
325                    },
326                    Self::OtherTypedLiteral {
327                        value: rv,
328                        datatype: rd,
329                    },
330                ) => lv == rv && ld == rd,
331                #[cfg(feature = "rdf-star")]
332                (Self::Triple(l), Self::Triple(r)) => l == r,
333                (_, _) => unreachable!(),
334            }
335    }
336}
337
338impl Eq for ExpressionTerm {}
339
340impl Hash for ExpressionTerm {
341    #[inline]
342    fn hash<H: Hasher>(&self, state: &mut H) {
343        discriminant(self).hash(state);
344        match self {
345            ExpressionTerm::NamedNode(v) => v.hash(state),
346            ExpressionTerm::BlankNode(v) => v.hash(state),
347            ExpressionTerm::StringLiteral(v) => v.hash(state),
348            ExpressionTerm::LangStringLiteral { value, language } => (value, language).hash(state),
349            ExpressionTerm::BooleanLiteral(v) => v.hash(state),
350            ExpressionTerm::IntegerLiteral(v) => v.hash(state),
351            ExpressionTerm::DecimalLiteral(v) => v.hash(state),
352            ExpressionTerm::FloatLiteral(v) => v.to_be_bytes().hash(state),
353            ExpressionTerm::DoubleLiteral(v) => v.to_be_bytes().hash(state),
354            ExpressionTerm::DateTimeLiteral(v) => v.hash(state),
355            #[cfg(feature = "sep-0002")]
356            ExpressionTerm::DateLiteral(v) => v.hash(state),
357            #[cfg(feature = "sep-0002")]
358            ExpressionTerm::TimeLiteral(v) => v.hash(state),
359            #[cfg(feature = "calendar-ext")]
360            ExpressionTerm::GYearLiteral(v) => v.hash(state),
361            #[cfg(feature = "calendar-ext")]
362            ExpressionTerm::GYearMonthLiteral(v) => v.hash(state),
363            #[cfg(feature = "calendar-ext")]
364            ExpressionTerm::GMonthLiteral(v) => v.hash(state),
365            #[cfg(feature = "calendar-ext")]
366            ExpressionTerm::GMonthDayLiteral(v) => v.hash(state),
367            #[cfg(feature = "calendar-ext")]
368            ExpressionTerm::GDayLiteral(v) => v.hash(state),
369            #[cfg(feature = "sep-0002")]
370            ExpressionTerm::DurationLiteral(v) => v.hash(state),
371            #[cfg(feature = "sep-0002")]
372            ExpressionTerm::YearMonthDurationLiteral(v) => v.hash(state),
373            #[cfg(feature = "sep-0002")]
374            ExpressionTerm::DayTimeDurationLiteral(v) => v.hash(state),
375            ExpressionTerm::OtherTypedLiteral { value, datatype } => (value, datatype).hash(state),
376            #[cfg(feature = "rdf-star")]
377            ExpressionTerm::Triple(v) => v.hash(state),
378        }
379    }
380}
381
382impl From<Term> for ExpressionTerm {
383    #[inline]
384    fn from(term: Term) -> Self {
385        match term {
386            Term::NamedNode(t) => Self::NamedNode(t),
387            Term::BlankNode(t) => Self::BlankNode(t),
388            Term::Literal(t) => {
389                let (value, datatype, language) = t.destruct();
390                if let Some(language) = language {
391                    Self::LangStringLiteral { value, language }
392                } else if let Some(datatype) = datatype {
393                    parse_typed_literal(&value, datatype.as_str())
394                        .unwrap_or(Self::OtherTypedLiteral { value, datatype })
395                } else {
396                    Self::StringLiteral(value)
397                }
398            }
399            #[cfg(feature = "rdf-star")]
400            Term::Triple(t) => Self::Triple(Box::new((*t).into())),
401        }
402    }
403}
404
405impl From<ExpressionTerm> for Term {
406    #[inline]
407    fn from(term: ExpressionTerm) -> Self {
408        match term {
409            ExpressionTerm::NamedNode(t) => t.into(),
410            ExpressionTerm::BlankNode(t) => t.into(),
411            ExpressionTerm::StringLiteral(value) => Literal::from(value).into(),
412            ExpressionTerm::LangStringLiteral { value, language } => {
413                Literal::new_language_tagged_literal_unchecked(value, language).into()
414            }
415            ExpressionTerm::BooleanLiteral(value) => Literal::from(value).into(),
416            ExpressionTerm::IntegerLiteral(value) => Literal::from(value).into(),
417            ExpressionTerm::DecimalLiteral(value) => Literal::from(value).into(),
418            ExpressionTerm::FloatLiteral(value) => Literal::from(value).into(),
419            ExpressionTerm::DoubleLiteral(value) => Literal::from(value).into(),
420            ExpressionTerm::DateTimeLiteral(value) => Literal::from(value).into(),
421            #[cfg(feature = "sep-0002")]
422            ExpressionTerm::DateLiteral(value) => Literal::from(value).into(),
423            #[cfg(feature = "sep-0002")]
424            ExpressionTerm::TimeLiteral(value) => Literal::from(value).into(),
425            #[cfg(feature = "calendar-ext")]
426            ExpressionTerm::GYearLiteral(value) => Literal::from(value).into(),
427            #[cfg(feature = "calendar-ext")]
428            ExpressionTerm::GYearMonthLiteral(value) => Literal::from(value).into(),
429            #[cfg(feature = "calendar-ext")]
430            ExpressionTerm::GMonthLiteral(value) => Literal::from(value).into(),
431            #[cfg(feature = "calendar-ext")]
432            ExpressionTerm::GMonthDayLiteral(value) => Literal::from(value).into(),
433            #[cfg(feature = "calendar-ext")]
434            ExpressionTerm::GDayLiteral(value) => Literal::from(value).into(),
435            #[cfg(feature = "sep-0002")]
436            ExpressionTerm::DurationLiteral(value) => Literal::from(value).into(),
437            #[cfg(feature = "sep-0002")]
438            ExpressionTerm::YearMonthDurationLiteral(value) => Literal::from(value).into(),
439            #[cfg(feature = "sep-0002")]
440            ExpressionTerm::DayTimeDurationLiteral(value) => Literal::from(value).into(),
441            ExpressionTerm::OtherTypedLiteral { value, datatype } => {
442                Literal::new_typed_literal(value, datatype).into()
443            }
444            #[cfg(feature = "rdf-star")]
445            ExpressionTerm::Triple(t) => Triple::from(*t).into(),
446        }
447    }
448}
449
450impl From<NamedNode> for ExpressionTerm {
451    #[inline]
452    fn from(term: NamedNode) -> Self {
453        Self::NamedNode(term)
454    }
455}
456impl From<bool> for ExpressionTerm {
457    fn from(value: bool) -> Self {
458        Self::BooleanLiteral(value.into())
459    }
460}
461
462impl ExpressionTerm {
463    /// Computes the term [Effective boolean value](https://www.w3.org/TR/sparql11-query/#ebv)
464    pub(crate) fn effective_boolean_value(&self) -> Option<bool> {
465        match self {
466            ExpressionTerm::BooleanLiteral(value) => Some((*value).into()),
467            ExpressionTerm::StringLiteral(value) => Some(!value.is_empty()),
468            ExpressionTerm::FloatLiteral(value) => Some(Boolean::from(*value).into()),
469            ExpressionTerm::DoubleLiteral(value) => Some(Boolean::from(*value).into()),
470            ExpressionTerm::IntegerLiteral(value) => Some(Boolean::from(*value).into()),
471            ExpressionTerm::DecimalLiteral(value) => Some(Boolean::from(*value).into()),
472            _ => None,
473        }
474    }
475}
476
477fn parse_typed_literal(value: &str, datatype: &str) -> Option<ExpressionTerm> {
478    Some(match datatype {
479        "http://www.w3.org/2001/XMLSchema#boolean" => {
480            ExpressionTerm::BooleanLiteral(value.parse().ok()?)
481        }
482        "http://www.w3.org/2001/XMLSchema#string" => ExpressionTerm::StringLiteral(value.into()),
483        "http://www.w3.org/2001/XMLSchema#float" => {
484            ExpressionTerm::FloatLiteral(value.parse().ok()?)
485        }
486        "http://www.w3.org/2001/XMLSchema#double" => {
487            ExpressionTerm::DoubleLiteral(value.parse().ok()?)
488        }
489        "http://www.w3.org/2001/XMLSchema#decimal" => {
490            ExpressionTerm::DecimalLiteral(value.parse().ok()?)
491        }
492        "http://www.w3.org/2001/XMLSchema#integer"
493        | "http://www.w3.org/2001/XMLSchema#byte"
494        | "http://www.w3.org/2001/XMLSchema#short"
495        | "http://www.w3.org/2001/XMLSchema#int"
496        | "http://www.w3.org/2001/XMLSchema#long"
497        | "http://www.w3.org/2001/XMLSchema#unsignedByte"
498        | "http://www.w3.org/2001/XMLSchema#unsignedShort"
499        | "http://www.w3.org/2001/XMLSchema#unsignedInt"
500        | "http://www.w3.org/2001/XMLSchema#unsignedLong"
501        | "http://www.w3.org/2001/XMLSchema#positiveInteger"
502        | "http://www.w3.org/2001/XMLSchema#negativeInteger"
503        | "http://www.w3.org/2001/XMLSchema#nonPositiveInteger"
504        | "http://www.w3.org/2001/XMLSchema#nonNegativeInteger" => {
505            ExpressionTerm::IntegerLiteral(value.parse().ok()?)
506        }
507        "http://www.w3.org/2001/XMLSchema#dateTime"
508        | "http://www.w3.org/2001/XMLSchema#dateTimeStamp" => {
509            ExpressionTerm::DateTimeLiteral(value.parse().ok()?)
510        }
511        #[cfg(feature = "sep-0002")]
512        "http://www.w3.org/2001/XMLSchema#time" => ExpressionTerm::TimeLiteral(value.parse().ok()?),
513        #[cfg(feature = "sep-0002")]
514        "http://www.w3.org/2001/XMLSchema#date" => ExpressionTerm::DateLiteral(value.parse().ok()?),
515        #[cfg(feature = "calendar-ext")]
516        "http://www.w3.org/2001/XMLSchema#gYearMonth" => {
517            ExpressionTerm::GYearMonthLiteral(value.parse().ok()?)
518        }
519        #[cfg(feature = "calendar-ext")]
520        "http://www.w3.org/2001/XMLSchema#gYear" => {
521            ExpressionTerm::GYearLiteral(value.parse().ok()?)
522        }
523        #[cfg(feature = "calendar-ext")]
524        "http://www.w3.org/2001/XMLSchema#gMonthDay" => {
525            ExpressionTerm::GMonthDayLiteral(value.parse().ok()?)
526        }
527        #[cfg(feature = "calendar-ext")]
528        "http://www.w3.org/2001/XMLSchema#gDay" => ExpressionTerm::GDayLiteral(value.parse().ok()?),
529        #[cfg(feature = "calendar-ext")]
530        "http://www.w3.org/2001/XMLSchema#gMonth" => {
531            ExpressionTerm::GMonthLiteral(value.parse().ok()?)
532        }
533        #[cfg(feature = "sep-0002")]
534        "http://www.w3.org/2001/XMLSchema#duration" => {
535            ExpressionTerm::DurationLiteral(value.parse().ok()?)
536        }
537        #[cfg(feature = "sep-0002")]
538        "http://www.w3.org/2001/XMLSchema#yearMonthDuration" => {
539            ExpressionTerm::YearMonthDurationLiteral(value.parse().ok()?)
540        }
541        #[cfg(feature = "sep-0002")]
542        "http://www.w3.org/2001/XMLSchema#dayTimeDuration" => {
543            ExpressionTerm::DayTimeDurationLiteral(value.parse().ok()?)
544        }
545        _ => return None,
546    })
547}
548
549#[cfg(feature = "rdf-star")]
550#[derive(Clone, PartialEq, Eq, Hash)]
551pub struct ExpressionTriple {
552    pub subject: ExpressionSubject,
553    pub predicate: NamedNode,
554    pub object: ExpressionTerm,
555}
556
557#[cfg(feature = "rdf-star")]
558impl From<ExpressionTriple> for ExpressionTerm {
559    #[inline]
560    fn from(triple: ExpressionTriple) -> Self {
561        Self::Triple(Box::new(triple))
562    }
563}
564
565#[cfg(feature = "rdf-star")]
566impl From<Triple> for ExpressionTriple {
567    #[inline]
568    fn from(triple: Triple) -> Self {
569        ExpressionTriple {
570            subject: triple.subject.into(),
571            predicate: triple.predicate,
572            object: triple.object.into(),
573        }
574    }
575}
576
577#[cfg(feature = "rdf-star")]
578impl From<ExpressionTriple> for Triple {
579    #[inline]
580    fn from(triple: ExpressionTriple) -> Self {
581        Triple {
582            subject: triple.subject.into(),
583            predicate: triple.predicate,
584            object: triple.object.into(),
585        }
586    }
587}
588
589#[cfg(feature = "rdf-star")]
590impl ExpressionTriple {
591    pub fn new(
592        subject: ExpressionTerm,
593        predicate: ExpressionTerm,
594        object: ExpressionTerm,
595    ) -> Option<Self> {
596        if !matches!(
597            subject,
598            ExpressionTerm::NamedNode(_) | ExpressionTerm::BlankNode(_) | ExpressionTerm::Triple(_)
599        ) {
600            return None;
601        }
602        if !matches!(predicate, ExpressionTerm::NamedNode(_)) {
603            return None;
604        }
605        Some(Self {
606            subject: match subject {
607                ExpressionTerm::NamedNode(s) => ExpressionSubject::NamedNode(s),
608                ExpressionTerm::BlankNode(s) => ExpressionSubject::BlankNode(s),
609                ExpressionTerm::Triple(s) => ExpressionSubject::Triple(s),
610                _ => return None,
611            },
612            predicate: if let ExpressionTerm::NamedNode(p) = predicate {
613                p
614            } else {
615                return None;
616            },
617            object,
618        })
619    }
620}
621
622#[cfg(feature = "rdf-star")]
623#[derive(Clone, PartialEq, Eq, Hash)]
624pub enum ExpressionSubject {
625    NamedNode(NamedNode),
626    BlankNode(BlankNode),
627    Triple(Box<ExpressionTriple>),
628}
629
630#[cfg(feature = "rdf-star")]
631impl From<ExpressionSubject> for ExpressionTerm {
632    #[inline]
633    fn from(subject: ExpressionSubject) -> Self {
634        match subject {
635            ExpressionSubject::NamedNode(s) => Self::NamedNode(s),
636            ExpressionSubject::BlankNode(s) => Self::BlankNode(s),
637            ExpressionSubject::Triple(s) => Self::Triple(s),
638        }
639    }
640}
641
642#[cfg(feature = "rdf-star")]
643impl From<ExpressionSubject> for Subject {
644    #[inline]
645    fn from(subject: ExpressionSubject) -> Self {
646        match subject {
647            ExpressionSubject::NamedNode(s) => s.into(),
648            ExpressionSubject::BlankNode(s) => s.into(),
649            ExpressionSubject::Triple(s) => Triple::from(*s).into(),
650        }
651    }
652}
653
654#[cfg(feature = "rdf-star")]
655impl From<Subject> for ExpressionSubject {
656    #[inline]
657    fn from(subject: Subject) -> Self {
658        match subject {
659            Subject::NamedNode(s) => Self::NamedNode(s),
660            Subject::BlankNode(s) => Self::BlankNode(s),
661            Subject::Triple(s) => ExpressionSubject::Triple(Box::new(ExpressionTriple::from(*s))),
662        }
663    }
664}