oxigraph/sparql/
service.rs1use crate::model::NamedNode;
2use crate::sparql::algebra::Query;
3use crate::sparql::error::EvaluationError;
4use crate::sparql::http::Client;
5use crate::sparql::model::QueryResults;
6use crate::sparql::results::QueryResultsFormat;
7use crate::sparql::QueryDataset;
8use oxiri::Iri;
9use sparesults::{QueryResultsParser, ReaderQueryResultsParserOutput};
10use spareval::{DefaultServiceHandler, QueryEvaluationError, QuerySolutionIter};
11use spargebra::algebra::GraphPattern;
12use std::error::Error;
13use std::time::Duration;
14
15pub trait ServiceHandler: Send + Sync {
63 type Error: Error + Send + Sync + 'static;
65
66 fn handle(&self, service_name: NamedNode, query: Query) -> Result<QueryResults, Self::Error>;
68}
69
70pub struct WrappedDefaultServiceHandler<H: ServiceHandler>(pub H);
71
72impl<H: ServiceHandler> DefaultServiceHandler for WrappedDefaultServiceHandler<H> {
73 type Error = QueryEvaluationError;
74
75 fn handle(
76 &self,
77 service_name: NamedNode,
78 pattern: GraphPattern,
79 base_iri: Option<String>,
80 ) -> Result<QuerySolutionIter, Self::Error> {
81 let QueryResults::Solutions(solutions) = self
82 .0
83 .handle(
84 service_name,
85 Query {
86 inner: spargebra::Query::Select {
87 dataset: None,
88 pattern,
89 base_iri: base_iri
90 .map(Iri::parse)
91 .transpose()
92 .map_err(|e| QueryEvaluationError::Service(Box::new(e)))?,
93 },
94 dataset: QueryDataset::new(),
95 },
96 )
97 .map_err(|e| QueryEvaluationError::Service(Box::new(e)))?
98 else {
99 return Err(QueryEvaluationError::Service(
100 "Only query solutions are supported in services".into(),
101 ));
102 };
103 Ok(solutions.into())
104 }
105}
106
107pub struct EmptyServiceHandler;
108
109impl DefaultServiceHandler for EmptyServiceHandler {
110 type Error = QueryEvaluationError;
111
112 fn handle(
113 &self,
114 service_name: NamedNode,
115
116 _: GraphPattern,
117 _: Option<String>,
118 ) -> Result<QuerySolutionIter, QueryEvaluationError> {
119 Err(QueryEvaluationError::UnsupportedService(service_name))
120 }
121}
122
123pub struct SimpleServiceHandler {
124 client: Client,
125}
126
127impl SimpleServiceHandler {
128 pub fn new(http_timeout: Option<Duration>, http_redirection_limit: usize) -> Self {
129 Self {
130 client: Client::new(http_timeout, http_redirection_limit),
131 }
132 }
133}
134
135impl DefaultServiceHandler for SimpleServiceHandler {
136 type Error = EvaluationError;
137
138 fn handle(
139 &self,
140 service_name: NamedNode,
141 pattern: GraphPattern,
142 base_iri: Option<String>,
143 ) -> Result<QuerySolutionIter, Self::Error> {
144 let (content_type, body) = self
145 .client
146 .post(
147 service_name.as_str(),
148 spargebra::Query::Select {
149 dataset: None,
150 pattern,
151 base_iri: base_iri
152 .map(Iri::parse)
153 .transpose()
154 .map_err(|e| EvaluationError::Service(Box::new(e)))?,
155 }
156 .to_string()
157 .into_bytes(),
158 "application/sparql-query",
159 "application/sparql-results+json, application/sparql-results+xml",
160 )
161 .map_err(|e| EvaluationError::Service(Box::new(e)))?;
162 let format = QueryResultsFormat::from_media_type(&content_type)
163 .ok_or_else(|| EvaluationError::UnsupportedContentType(content_type))?;
164 let ReaderQueryResultsParserOutput::Solutions(reader) =
165 QueryResultsParser::from_format(format).for_reader(body)?
166 else {
167 return Err(EvaluationError::ServiceDoesNotReturnSolutions);
168 };
169 Ok(QuerySolutionIter::new(
170 reader.variables().into(),
171 Box::new(reader.map(|t| t.map_err(|e| QueryEvaluationError::Service(Box::new(e))))),
172 ))
173 }
174}