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}