Skip to content

Commit

Permalink
Merge c36a147 into c877b06
Browse files Browse the repository at this point in the history
  • Loading branch information
pchampin committed Aug 28, 2020
2 parents c877b06 + c36a147 commit 643ff13
Show file tree
Hide file tree
Showing 2 changed files with 140 additions and 0 deletions.
1 change: 1 addition & 0 deletions api/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
139 changes: 139 additions & 0 deletions api/src/sparql.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
//! 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:
//!
//! - preparing a query for multiple use;
//! - setting default values for `BASE`, `PREFIX`, `FROM`, `FROM NAMED` directives,
//! before parsing query string;
//! - 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.

use crate::term::TTerm;
use crate::triple::stream::TripleSource;
use std::error::Error;

/// A dataset that can be queried with SPARQL.
pub trait SparqlDataset {
type BindingsTerm: TTerm;
type BindingsResult: SparqlBindings<Self>;
type TriplesResult: TripleSource;
type SparqlError: Error + 'static;

/// Parse and immediately execute `query`
fn query(&self, query: &str) -> Result<SparqlResult<Self>, Self::SparqlError>;
}

/// 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>;
}

/// A dummy module to check that implementing these traits is actually possible
#[cfg(test)]
mod dummy {
use super::*;
use crate::dataset::Dataset;
use std::convert::Infallible;

pub type MyTerm = crate::term::test::TestTerm<String>;
pub type MyQuad = ([MyTerm; 3], Option<MyTerm>);
pub type MyDataset = Vec<MyQuad>;

pub struct MyBindings(Box<dyn Iterator<Item = Result<Vec<Option<MyTerm>>, Infallible>>>);

impl IntoIterator for MyBindings {
type Item = Result<Vec<Option<MyTerm>>, Infallible>;
type IntoIter = Box<dyn Iterator<Item = Result<Vec<Option<MyTerm>>, Infallible>>>;
fn into_iter(self) -> Self::IntoIter {
self.0
}
}
impl SparqlBindings<MyDataset> for MyBindings {
fn variables(&self) -> Vec<&str> {
vec!["s"]
}
}

impl SparqlDataset for MyDataset {
type BindingsTerm = MyTerm;
type BindingsResult = MyBindings;
type TriplesResult = Box<dyn Iterator<Item = Result<[MyTerm; 3], Infallible>>>;
type SparqlError = Infallible;

fn query(&self, query: &str) -> Result<SparqlResult<Self>, Self::SparqlError> {
match query {
"ASK" => Ok(SparqlResult::Boolean(true)),
"GRAPH" => Ok(SparqlResult::Triples(Box::new(
self.clone().into_iter().map(|q| Ok(q.0)),
)
as Self::TriplesResult)),
_ => Ok(SparqlResult::Bindings(MyBindings(Box::new(
self.subjects()
.unwrap()
.into_iter()
.map(|t| Ok(vec![Some(t)])),
)))),
}
}
}
}

0 comments on commit 643ff13

Please sign in to comment.