qualia_core_db/sparql_library/
sparql_endpoint.rs1use crate::sparql_ast::*;
6use crate::sparql_executor::*;
7use crate::sparql_library::serialisers::sparql_results::ResultFormatter;
8use crate::sparql_parser;
9use crate::sparql_planner::*;
10use crate::NQuin;
11
12pub struct SparqlEndpoint<'a> {
14 quins: &'a [NQuin],
15}
16
17impl<'a> SparqlEndpoint<'a> {
18 pub fn new(quins: &'a [NQuin]) -> Self {
19 Self { quins }
20 }
21
22 pub fn handle_query(&self, query: &str, format: &str) -> Result<String, String> {
24 let (sparql_query, ctx) = sparql_parser::parse_sparql(query)?;
26
27 let plan = QueryPlanner::plan(&sparql_query, &ctx)?;
29
30 let executor = QueryExecutor::new(&self.quins);
32 let results = executor.execute(&plan, &ctx)?;
33
34 match format.to_lowercase().as_str() {
36 "xml" => {
37 let mut output = Vec::new();
38 let vars = match &sparql_query {
39 SparqlQuery::Select(select) => {
40 select.variables[..select.var_count as usize].to_vec()
41 }
42 _ => vec![],
43 };
44 ResultFormatter::format_xml(&mut output, &vars, &results, &ctx, None)
45 .map_err(|e| e.to_string())?;
46 Ok(String::from_utf8(output).unwrap())
47 }
48 "json" => {
49 let mut output = Vec::new();
50 let vars = match &sparql_query {
51 SparqlQuery::Select(select) => {
52 select.variables[..select.var_count as usize].to_vec()
53 }
54 _ => vec![],
55 };
56 ResultFormatter::format_json(&mut output, &vars, &results, &ctx, None)
57 .map_err(|e| e.to_string())?;
58 Ok(String::from_utf8(output).unwrap())
59 }
60 "tsv" => {
61 let mut output = Vec::new();
62 let vars = match &sparql_query {
63 SparqlQuery::Select(select) => {
64 select.variables[..select.var_count as usize].to_vec()
65 }
66 _ => vec![],
67 };
68 ResultFormatter::format_tsv(&mut output, &vars, &results, &ctx, None)
69 .map_err(|e| e.to_string())?;
70 Ok(String::from_utf8(output).unwrap())
71 }
72 "csv" => {
73 let mut output = Vec::new();
74 let vars = match &sparql_query {
75 SparqlQuery::Select(select) => {
76 select.variables[..select.var_count as usize].to_vec()
77 }
78 _ => vec![],
79 };
80 ResultFormatter::format_csv(&mut output, &vars, &results, &ctx, None)
81 .map_err(|e| e.to_string())?;
82 Ok(String::from_utf8(output).unwrap())
83 }
84 _ => Err("Unsupported format. Use: xml, json, tsv, or csv".to_string()),
85 }
86 }
87
88 pub fn handle_ask(&self, query: &str, format: &str) -> Result<String, String> {
90 let (sparql_query, ctx) = sparql_parser::parse_sparql(query)?;
91
92 let plan = QueryPlanner::plan(&sparql_query, &ctx)?;
93 let executor = QueryExecutor::new(&self.quins);
94 let has_results = executor.execute_ask(&plan, &ctx)?;
95
96 match format.to_lowercase().as_str() {
97 "xml" => {
98 let mut output = Vec::new();
99 ResultFormatter::format_ask_xml(&mut output, has_results)
100 .map_err(|e| e.to_string())?;
101 Ok(String::from_utf8(output).unwrap())
102 }
103 "json" => {
104 let mut output = Vec::new();
105 ResultFormatter::format_ask_json(&mut output, has_results)
106 .map_err(|e| e.to_string())?;
107 Ok(String::from_utf8(output).unwrap())
108 }
109 _ => Err("Unsupported format for ASK. Use: xml or json".to_string()),
110 }
111 }
112
113 pub fn handle_construct(&self, query: &str, format: &str) -> Result<String, String> {
115 let (sparql_query, ctx) = sparql_parser::parse_sparql(query)?;
116
117 let template_pattern = match &sparql_query {
118 SparqlQuery::Construct(c) => c.template_pattern,
119 _ => return Err("Query is not a CONSTRUCT".to_string()),
120 };
121
122 let plan = QueryPlanner::plan(&sparql_query, &ctx)?;
123 let executor = QueryExecutor::new(&self.quins);
124 let results = executor.execute_construct(&plan, &ctx, template_pattern)?;
126
127 match format.to_lowercase().as_str() {
128 "ntriples" => {
129 let mut output = Vec::new();
130 ResultFormatter::format_ntriples(&mut output, &results)
131 .map_err(|e| e.to_string())?;
132 Ok(String::from_utf8(output).unwrap())
133 }
134 "xml" => {
135 let mut output = Vec::new();
136 let vars = vec![0u8, 1u8, 2u8]; ResultFormatter::format_xml(&mut output, &vars, &results, &ctx, None)
139 .map_err(|e| e.to_string())?;
140 Ok(String::from_utf8(output).unwrap())
141 }
142 "json" => {
143 let mut output = Vec::new();
144 let vars = vec![0u8, 1u8, 2u8];
145 ResultFormatter::format_json(&mut output, &vars, &results, &ctx, None)
146 .map_err(|e| e.to_string())?;
147 Ok(String::from_utf8(output).unwrap())
148 }
149 _ => Err("Unsupported format. Use: ntriples, xml, or json".to_string()),
150 }
151 }
152
153 pub fn handle_describe(&self, query: &str, format: &str) -> Result<String, String> {
155 let (sparql_query, ctx) = sparql_parser::parse_sparql(query)?;
156
157 let describe = match &sparql_query {
158 SparqlQuery::Describe(d) => *d,
159 _ => return Err("Query is not a DESCRIBE".to_string()),
160 };
161
162 let plan = QueryPlanner::plan(&sparql_query, &ctx)?;
163 let executor = QueryExecutor::new(&self.quins);
164 let results = executor.execute_describe(&plan, &ctx, &describe)?;
166
167 match format.to_lowercase().as_str() {
168 "ntriples" => {
169 let mut output = Vec::new();
170 ResultFormatter::format_ntriples(&mut output, &results)
171 .map_err(|e| e.to_string())?;
172 Ok(String::from_utf8(output).unwrap())
173 }
174 "xml" => {
175 let mut output = Vec::new();
176 let vars = match &sparql_query {
177 SparqlQuery::Describe(describe) => {
178 if describe.var_count > 0 {
180 vec![describe.vars_or_ids[0] as VariableId]
181 } else {
182 vec![0]
183 }
184 }
185 _ => vec![],
186 };
187 ResultFormatter::format_xml(&mut output, &vars, &results, &ctx, None)
188 .map_err(|e| e.to_string())?;
189 Ok(String::from_utf8(output).unwrap())
190 }
191 "json" => {
192 let mut output = Vec::new();
193 let vars = match &sparql_query {
194 SparqlQuery::Describe(describe) => {
195 if describe.var_count > 0 {
196 vec![describe.vars_or_ids[0] as VariableId]
197 } else {
198 vec![0]
199 }
200 }
201 _ => vec![],
202 };
203 ResultFormatter::format_json(&mut output, &vars, &results, &ctx, None)
204 .map_err(|e| e.to_string())?;
205 Ok(String::from_utf8(output).unwrap())
206 }
207 _ => Err("Unsupported format. Use: ntriples, xml, or json".to_string()),
208 }
209 }
210}
211
212pub struct SparqlProtocolHandler<'a> {
214 endpoint: SparqlEndpoint<'a>,
215}
216
217impl<'a> SparqlProtocolHandler<'a> {
218 pub fn new(quins: &'a [NQuin]) -> Self {
219 Self {
220 endpoint: SparqlEndpoint::new(quins),
221 }
222 }
223
224 pub fn parse_accept_header(accept: &str) -> String {
226 if accept.contains("application/sparql-results+xml") {
227 "xml".to_string()
228 } else if accept.contains("application/sparql-results+json") {
229 "json".to_string()
230 } else if accept.contains("text/tab-separated-values") {
231 "tsv".to_string()
232 } else if accept.contains("text/csv") {
233 "csv".to_string()
234 } else if accept.contains("application/n-triples") {
235 "ntriples".to_string()
236 } else if accept.contains("application/n-quads") {
237 "ntriples".to_string()
238 } else {
239 "json".to_string() }
241 }
242
243 pub fn handle_request(
245 &self,
246 query: Option<&str>,
247 accept: Option<&str>,
248 ) -> Result<String, String> {
249 let query = query.ok_or("No query provided")?;
250 let format = accept
251 .map(|a| Self::parse_accept_header(a))
252 .unwrap_or_else(|| "json".to_string());
253
254 let query_upper = query.trim().to_uppercase();
256
257 if query_upper.starts_with("ASK") {
258 self.endpoint.handle_ask(query, &format)
259 } else if query_upper.starts_with("CONSTRUCT") {
260 self.endpoint.handle_construct(query, &format)
261 } else if query_upper.starts_with("DESCRIBE") {
262 self.endpoint.handle_describe(query, &format)
263 } else {
264 self.endpoint.handle_query(query, &format)
266 }
267 }
268}
269
270#[cfg(test)]
271mod tests {
272 use super::*;
273
274 #[test]
275 fn test_parse_accept_header() {
276 assert_eq!(
277 SparqlProtocolHandler::parse_accept_header("application/sparql-results+xml"),
278 "xml"
279 );
280 assert_eq!(
281 SparqlProtocolHandler::parse_accept_header("application/sparql-results+json"),
282 "json"
283 );
284 assert_eq!(
285 SparqlProtocolHandler::parse_accept_header("text/tab-separated-values"),
286 "tsv"
287 );
288 assert_eq!(
289 SparqlProtocolHandler::parse_accept_header("text/csv"),
290 "csv"
291 );
292 assert_eq!(
293 SparqlProtocolHandler::parse_accept_header("application/n-triples"),
294 "ntriples"
295 );
296 assert_eq!(
297 SparqlProtocolHandler::parse_accept_header("application/n-quads"),
298 "ntriples"
299 );
300 }
301}