sophia_api/sparql.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182
//! Common traits for working with [SPARQL](https://www.w3.org/TR/sparql11-query/).
//!
//! # Design rationale
//!
//! These traits are deliberately very generic.
//! Specific implementations may have additional features, such as:
//!
//! - setting default values for `BASE`, `PREFIX`, `FROM`, `FROM NAMED` directives,
//! before parsing query string, or
//! - pre-binding variables before evaluating query;
//! - etc...
//!
//! However, we do not want to impose these feature, or any subset thereof,
//! to all implementations of Sophia.
//!
//! # Extension point
//!
//! A possible way to extend these traits with additional functionalities
//! (such as the ones described above)
//! would be to define subtraits of `Query` with additional methods
//! (*e.g.*`set_base`, `bind_variables`...).
//! Implementation could then express requirements as trait bound, e.g.:
//! ```ignore
//! D: SparqlDataset,
//! D::Query: Clone + BindVariable,
//! ```
//!
//! Sophia may define such traits in the future.
use crate::source::TripleSource;
use crate::term::Term;
use std::borrow::Borrow;
use std::error::Error;
/// A dataset that can be queried with SPARQL.
pub trait SparqlDataset {
/// The type of terms that SELECT queries will return.
type BindingsTerm: Term;
/// The type of bindings that SELECT queries will return.
type BindingsResult: SparqlBindings<Self>;
/// The type of triples that GRAPH and DESCRIBE queries will return.
type TriplesResult: TripleSource;
/// The type of errors that processing SPARQL queries may raise.
type SparqlError: Error + 'static;
/// The type representing pre-processed queries.
///
/// See [`prepare_query`](#tymethod.prepare_query) for more defail.
type Query: Query<Error = Self::SparqlError>;
/// Parse and immediately execute `query`.
///
/// `query` is usually either a `&str` that will be parsed on the fly,
/// or a `Self::Query` that was earlier prepared by the [`prepare_query`] method.
///
/// [`prepare_query`]: #method.prepared
fn query<Q>(&self, query: Q) -> Result<SparqlResult<Self>, Self::SparqlError>
where
Q: IntoQuery<Self::Query>;
/// Prepare a query for multiple future executions.
///
/// This allows some implementation to separate parsing,
/// (or any other pre-processing step)
/// of the query string from the actual exectution of the query.
/// There is however no guarantee on how much pre-processing is actually done by this method
/// (see below).
///
/// # Note to implementers
///
/// If it is impossible or inconvenient to provide a type for pre-parsed queries,
/// you can still use `String`, which implements the [`Query`] trait.
fn prepare_query(&self, query_string: &str) -> Result<Self::Query, Self::SparqlError> {
Self::Query::parse(query_string)
}
}
/// Preprocessed query, ready for execution.
///
/// This trait exist to allow *some* implementations of [`SparqlDataset`]
/// to mutualize the parsing of queries in the
/// [`prepare_query`](SparqlDataset::prepare_query) method.
pub trait Query: Sized {
/// The error type that might be raised when parsing a query.
type Error: Error + 'static;
/// Parse the given text into a [`Query`].
fn parse(query_source: &str) -> Result<Self, Self::Error>;
}
impl Query for String {
type Error = std::convert::Infallible;
fn parse(query_source: &str) -> Result<Self, Self::Error> {
Ok(query_source.into())
}
}
/// A utility trait to allow [`SparqlDataset::query`]
/// to accept either `&str` or [`Self::Query`](SparqlDataset::Query).
pub trait IntoQuery<Q: Query> {
/// The ouput type of [`into_query`](IntoQuery::into_query).
type Out: Borrow<Q>;
/// Convert `self` to a [`Query`].
fn into_query(self) -> Result<Self::Out, Q::Error>;
}
impl<'a, Q> IntoQuery<Q> for &'a Q
where
Q: Query,
{
type Out = &'a Q;
fn into_query(self) -> Result<Self::Out, Q::Error> {
Ok(self)
}
}
impl<'a, Q> IntoQuery<Q> for &'a str
where
Q: Query,
{
type Out = Q;
fn into_query(self) -> Result<Self::Out, Q::Error> {
Q::parse(self)
}
}
/// The result of executing a SPARQL query.
pub enum SparqlResult<T>
where
T: SparqlDataset + ?Sized,
{
/// The result of a SELECT query
Bindings(T::BindingsResult),
/// The result of an ASK query
Boolean(bool),
/// The result of a CONSTRUCT or DESCRIBE query
Triples(T::TriplesResult),
}
impl<T> SparqlResult<T>
where
T: SparqlDataset + ?Sized,
{
/// Get this result as a `Bindings`.
///
/// # Panics
/// This will panic if `self` is actually of another kind.
pub fn into_bindings(self) -> T::BindingsResult {
match self {
SparqlResult::Bindings(b) => b,
_ => panic!("This SparqlResult is not a Bindings"),
}
}
/// Get this result as a `Boolean`.
///
/// # Panics
/// This will panic if `self` is actually of another kind.
pub fn into_boolean(self) -> bool {
match self {
SparqlResult::Boolean(b) => b,
_ => panic!("This SparqlResult is not a Boolean"),
}
}
/// Get this result as a `Triples`.
///
/// # Panics
/// This will panic if `self` is actually of another kind.
pub fn into_triples(self) -> T::TriplesResult {
match self {
SparqlResult::Triples(t) => t,
_ => panic!("This SparqlResult is not a Triples"),
}
}
}
/// The result of executing a SPARQL SELECT query
pub trait SparqlBindings<D>:
IntoIterator<Item = Result<Vec<Option<D::BindingsTerm>>, D::SparqlError>>
where
D: SparqlDataset + ?Sized,
{
/// Return the list of SELECTed variable names
fn variables(&self) -> Vec<&str>;
}