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
//! 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`...).
//! 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::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: 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 {
    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`.
pub trait IntoQuery<Q: Query> {
    type Out: Borrow<Q>;
    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>;
}