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