Skip to main content

spareval/
dataset.rs

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