Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
This is the result of the discussions in #90 and #91. Thanks to @Tpt and @yever for their valuable feedback.
- Loading branch information
Showing
2 changed files
with
182 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -39,5 +39,6 @@ pub mod parser; | |
pub mod prefix; | ||
pub mod quad; | ||
pub mod serializer; | ||
pub mod sparql; | ||
pub mod term; | ||
pub mod triple; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,181 @@ | ||
//! 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 implementation 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`...). | ||
//! | ||
//! Sophia may define such traits in the future, | ||
//! | ||
//! Sophia | ||
|
||
use crate::term::TTerm; | ||
use crate::triple::stream::TripleSource; | ||
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: TTerm; | ||
/// 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: ToQuery<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. | ||
/// | ||
/// [`Query`]: ./trait.Query.html | ||
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`] method. | ||
/// | ||
/// [`SparqlDataset`]: ./trait.SparqlDataset.html | ||
/// [`prepare_query`]: ./trait.SparqlDataset.html#tymethod.prepare_query | ||
pub trait Query: Sized { | ||
type Error: Error + 'static; | ||
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`]: ./trait.SparqlDataset.html#tymethod.query | ||
pub trait ToQuery<Q: Query> { | ||
type Out: Borrow<Q>; | ||
fn to_query(self) -> Result<Self::Out, Q::Error>; | ||
} | ||
|
||
impl<'a, Q> ToQuery<Q> for &'a Q | ||
where | ||
Q: Query, | ||
{ | ||
type Out = &'a Q; | ||
fn to_query(self) -> Result<Self::Out, Q::Error> { | ||
Ok(self) | ||
} | ||
} | ||
|
||
impl<'a, Q> ToQuery<Q> for &'a str | ||
where | ||
Q: Query, | ||
{ | ||
type Out = Q; | ||
fn to_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>; | ||
} |