spareval/
service.rs

1use crate::{QueryEvaluationError, QuerySolutionIter};
2use oxiri::Iri;
3use oxrdf::NamedNode;
4use spargebra::algebra::GraphPattern;
5use std::collections::HashMap;
6use std::error::Error;
7use std::sync::Arc;
8
9/// Handler for [SPARQL 1.1 Federated Query](https://www.w3.org/TR/sparql11-federated-query/) SERVICEs.
10///
11/// Should be given to [`QueryOptions`](super::QueryEvaluator::with_service_handler())
12/// before evaluating a SPARQL query that uses SERVICE calls.
13///
14/// Note that you can also use [`DefaultServiceHandler`] if you need to handle any service and not a specific one.
15///
16/// ```
17/// use oxiri::Iri;
18/// use oxrdf::{Dataset, Literal, NamedNode, Variable};
19/// use sparesults::QuerySolution;
20/// use spareval::{QueryEvaluator, QueryResults, QuerySolutionIter, ServiceHandler};
21/// use spargebra::SparqlParser;
22/// use spargebra::algebra::GraphPattern;
23/// use std::convert::Infallible;
24/// use std::iter::once;
25/// use std::sync::Arc;
26///
27/// struct TestServiceHandler {}
28///
29/// impl ServiceHandler for TestServiceHandler {
30///     type Error = Infallible;
31///
32///     fn handle(
33///         &self,
34///         _pattern: &GraphPattern,
35///         _base_iri: Option<&Iri<String>>,
36///     ) -> Result<QuerySolutionIter<'static>, Self::Error> {
37///         // Always return a single binding foo -> 1
38///         let variables = [Variable::new_unchecked("foo")].into();
39///         Ok(QuerySolutionIter::new(
40///             Arc::clone(&variables),
41///             once(Ok(QuerySolution::from((
42///                 variables,
43///                 vec![Some(Literal::from(1).into())],
44///             )))),
45///         ))
46///     }
47/// }
48///
49/// let evaluator = QueryEvaluator::default().with_service_handler(
50///     NamedNode::new("http://example.com/service")?,
51///     TestServiceHandler {},
52/// );
53/// let query = SparqlParser::new()
54///     .parse_query("SELECT ?foo WHERE { SERVICE <http://example.com/service> {} }")?;
55/// if let QueryResults::Solutions(mut solutions) =
56///     evaluator.prepare(&query).execute(&Dataset::new())?
57/// {
58///     assert_eq!(
59///         solutions.next().unwrap()?.get("foo"),
60///         Some(&Literal::from(1).into())
61///     );
62/// }
63/// # Result::<_, Box<dyn std::error::Error>>::Ok(())
64/// ```
65pub trait ServiceHandler: Send + Sync {
66    /// The service evaluation error.
67    type Error: Error + Send + Sync + 'static;
68
69    /// Evaluates a [`Query`](spargebra::Query) against the service.
70    fn handle(
71        &self,
72        pattern: &GraphPattern,
73        base_iri: Option<&Iri<String>>,
74    ) -> Result<QuerySolutionIter<'static>, Self::Error>;
75}
76
77/// Default handler for [SPARQL 1.1 Federated Query](https://www.w3.org/TR/sparql11-federated-query/) SERVICEs.
78///
79/// Should be given to [`QueryOptions`](super::QueryEvaluator::with_default_service_handler())
80/// before evaluating a SPARQL query that uses SERVICE calls.
81///
82/// Note that you can also use [`ServiceHandler`] if you need to handle a single service and not any service.
83///
84/// ```
85/// use oxiri::Iri;
86/// use oxrdf::{Dataset, NamedNode, Variable};
87/// use sparesults::QuerySolution;
88/// use spareval::{DefaultServiceHandler, QueryEvaluator, QueryResults, QuerySolutionIter};
89/// use spargebra::SparqlParser;
90/// use spargebra::algebra::GraphPattern;
91/// use std::convert::Infallible;
92/// use std::iter::once;
93/// use std::sync::Arc;
94///
95/// struct TestServiceHandler {}
96///
97/// impl DefaultServiceHandler for TestServiceHandler {
98///     type Error = Infallible;
99///
100///     fn handle(
101///         &self,
102///         service_name: &NamedNode,
103///         _pattern: &GraphPattern,
104///         _base_iri: Option<&Iri<String>>,
105///     ) -> Result<QuerySolutionIter<'static>, Self::Error> {
106///         // Always return a single binding name -> name of service
107///         let variables = [Variable::new_unchecked("foo")].into();
108///         Ok(QuerySolutionIter::new(
109///             Arc::clone(&variables),
110///             once(Ok(QuerySolution::from((
111///                 variables,
112///                 vec![Some(service_name.clone().into())],
113///             )))),
114///         ))
115///     }
116/// }
117///
118/// let evaluator = QueryEvaluator::default().with_default_service_handler(TestServiceHandler {});
119/// let query = SparqlParser::new()
120///     .parse_query("SELECT ?foo WHERE { SERVICE <http://example.com/service> {} }")?;
121/// if let QueryResults::Solutions(mut solutions) =
122///     evaluator.prepare(&query).execute(&Dataset::new())?
123/// {
124///     assert_eq!(
125///         solutions.next().unwrap()?.get("foo"),
126///         Some(&NamedNode::new("http://example.com/service")?.into())
127///     );
128/// }
129/// # Result::<_, Box<dyn std::error::Error>>::Ok(())
130/// ```
131pub trait DefaultServiceHandler: Send + Sync {
132    /// The service evaluation error.
133    type Error: Error + Send + Sync + 'static;
134
135    /// Evaluates a [`GraphPattern`] against a given service identified by a [`NamedNode`].
136    fn handle(
137        &self,
138        service_name: &NamedNode,
139        pattern: &GraphPattern,
140        base_iri: Option<&Iri<String>>,
141    ) -> Result<QuerySolutionIter<'static>, Self::Error>;
142}
143
144#[derive(Clone, Default)]
145pub struct ServiceHandlerRegistry {
146    default: Option<Arc<dyn DefaultServiceHandler<Error = QueryEvaluationError>>>,
147    handlers: HashMap<NamedNode, Arc<dyn ServiceHandler<Error = QueryEvaluationError>>>,
148}
149
150impl ServiceHandlerRegistry {
151    pub fn with_handler(
152        mut self,
153        service_name: NamedNode,
154        handler: impl ServiceHandler + 'static,
155    ) -> Self {
156        self.handlers.insert(
157            service_name,
158            Arc::new(ErrorConversionServiceHandler(handler)),
159        );
160        self
161    }
162
163    pub fn with_default_handler(mut self, default: impl DefaultServiceHandler + 'static) -> Self {
164        self.default = Some(Arc::new(ErrorConversionServiceHandler(default)));
165        self
166    }
167
168    pub fn has_default_handler(&self) -> bool {
169        self.default.is_some()
170    }
171
172    pub fn handle(
173        &self,
174        service_name: &NamedNode,
175        pattern: &GraphPattern,
176        base_iri: Option<&Iri<String>>,
177    ) -> Result<QuerySolutionIter<'static>, QueryEvaluationError> {
178        if let Some(handler) = self.handlers.get(service_name) {
179            return handler.handle(pattern, base_iri);
180        }
181        if let Some(default) = &self.default {
182            return default.handle(service_name, pattern, base_iri);
183        }
184        Err(QueryEvaluationError::UnsupportedService(
185            service_name.clone(),
186        ))
187    }
188}
189
190struct ErrorConversionServiceHandler<S>(S);
191
192impl<S: ServiceHandler> ServiceHandler for ErrorConversionServiceHandler<S> {
193    type Error = QueryEvaluationError;
194
195    fn handle(
196        &self,
197        pattern: &GraphPattern,
198        base_iri: Option<&Iri<String>>,
199    ) -> Result<QuerySolutionIter<'static>, QueryEvaluationError> {
200        self.0.handle(pattern, base_iri).map_err(wrap_service_error)
201    }
202}
203
204impl<S: DefaultServiceHandler> DefaultServiceHandler for ErrorConversionServiceHandler<S> {
205    type Error = QueryEvaluationError;
206
207    fn handle(
208        &self,
209        service_name: &NamedNode,
210        pattern: &GraphPattern,
211        base_iri: Option<&Iri<String>>,
212    ) -> Result<QuerySolutionIter<'static>, QueryEvaluationError> {
213        self.0
214            .handle(service_name, pattern, base_iri)
215            .map_err(wrap_service_error)
216    }
217}
218
219fn wrap_service_error(error: impl Error + Send + Sync + 'static) -> QueryEvaluationError {
220    let error: Box<dyn Error + Send + Sync> = Box::new(error);
221    match error.downcast() {
222        Ok(error) => *error,
223        Err(error) => QueryEvaluationError::Service(error),
224    }
225}