diff --git a/lib/src/sparql/eval.rs b/lib/src/sparql/eval.rs index 608cd901..3f00696d 100644 --- a/lib/src/sparql/eval.rs +++ b/lib/src/sparql/eval.rs @@ -1416,13 +1416,14 @@ impl<'a, S: StoreConnection + 'a> SimpleEvaluator { }), PlanExpression::CustomFunction { name, parameters } => { let parameters = parameters - .iter() - .map(|p| { - self.eval_expression(p, tuple) - .and_then(|encoded| self.dataset.decode_term(encoded).ok()) - }) - .collect::>(); - self.custom_functions_handler.handle(name, ¶meters) + .iter() + .map(|p| { + self.eval_expression(p, tuple) + .and_then(|encoded| self.dataset.decode_term(encoded).ok()) + }) + .collect::>(); + self.custom_functions_handler + .handle(name, ¶meters) .and_then(|term| self.dataset.encoder().encode_term(&term).ok()) } } diff --git a/lib/src/sparql/mod.rs b/lib/src/sparql/mod.rs index b1071a60..94d7cc7c 100644 --- a/lib/src/sparql/mod.rs +++ b/lib/src/sparql/mod.rs @@ -9,7 +9,7 @@ mod plan; mod plan_builder; mod xml_results; -use crate::model::{NamedNode,Term}; +use crate::model::{NamedNode, Term}; use crate::sparql::algebra::QueryVariants; use crate::sparql::eval::SimpleEvaluator; use crate::sparql::parser::read_sparql_query; @@ -70,7 +70,12 @@ impl<'a, S: StoreConnection + 'a> SimplePreparedQuery { SimplePreparedQueryAction::Select { plan, variables, - evaluator: SimpleEvaluator::new(dataset, base_iri, options.service_handler, options.custom_functions_handler), + evaluator: SimpleEvaluator::new( + dataset, + base_iri, + options.service_handler, + options.custom_functions_handler, + ), } } QueryVariants::Ask { @@ -81,7 +86,12 @@ impl<'a, S: StoreConnection + 'a> SimplePreparedQuery { let (plan, _) = PlanBuilder::build(dataset.encoder(), &algebra)?; SimplePreparedQueryAction::Ask { plan, - evaluator: SimpleEvaluator::new(dataset, base_iri, options.service_handler, options.custom_functions_handler), + evaluator: SimpleEvaluator::new( + dataset, + base_iri, + options.service_handler, + options.custom_functions_handler, + ), } } QueryVariants::Construct { @@ -98,7 +108,12 @@ impl<'a, S: StoreConnection + 'a> SimplePreparedQuery { &construct, variables, )?, - evaluator: SimpleEvaluator::new(dataset, base_iri, options.service_handler, options.custom_functions_handler), + evaluator: SimpleEvaluator::new( + dataset, + base_iri, + options.service_handler, + options.custom_functions_handler, + ), } } QueryVariants::Describe { @@ -109,7 +124,12 @@ impl<'a, S: StoreConnection + 'a> SimplePreparedQuery { let (plan, _) = PlanBuilder::build(dataset.encoder(), &algebra)?; SimplePreparedQueryAction::Describe { plan, - evaluator: SimpleEvaluator::new(dataset, base_iri, options.service_handler, options.custom_functions_handler), + evaluator: SimpleEvaluator::new( + dataset, + base_iri, + options.service_handler, + options.custom_functions_handler, + ), } } })) @@ -131,7 +151,12 @@ impl<'a, S: StoreConnection + 'a> SimplePreparedQuery { Ok(Self(SimplePreparedQueryAction::Select { plan, variables, - evaluator: SimpleEvaluator::new(dataset, base_iri, options.service_handler, options.custom_functions_handler), + evaluator: SimpleEvaluator::new( + dataset, + base_iri, + options.service_handler, + options.custom_functions_handler, + ), })) } } @@ -185,36 +210,35 @@ impl ServiceHandler for EmptyServiceHandler { } } - /// Handler for custom functions. -/// +/// /// For example, the following query uses a custom function to reverse a string: /// -///```sparql +///```sparql /// PREFIX ex: /// PREFIX foaf: /// SELECT ?name ?reverse /// WHERE -/// { +/// { /// ?s foaf:name ?name . /// BIND(ex:REVERSE(?name) as ?reverse) /// } /// ``` pub trait CustomFunctionsHandler { /// Handles all custom functions for a given query - /// + /// /// For the example above node would have the value http://example.com#REVERSE - /// and would recieve one parameter with the value mapped to ?name - /// + /// and would recieve one parameter with the value mapped to ?name + /// /// Returns `None` if there is an error or the given NamedNode isn't recognized - fn handle(&self, node: &NamedNode, parameters: &Vec>) -> Option; + fn handle(&self, node: &NamedNode, parameters: &[Option]) -> Option; } #[derive(Default)] struct EmptyCustomFunctionsHandler {} impl CustomFunctionsHandler for EmptyCustomFunctionsHandler { - fn handle(&self, _node: &NamedNode, _parameters: &Vec>) -> Option { + fn handle(&self, _node: &NamedNode, _parameters: &[Option]) -> Option { None } } @@ -258,12 +282,13 @@ impl<'a> QueryOptions<'a> { } /// Set a CustomFunctionHandler to add your own functions - pub fn with_custom_functions_handler(mut self, custom_functions_handler: Box) -> Self { + pub fn with_custom_functions_handler( + mut self, + custom_functions_handler: Box, + ) -> Self { self.custom_functions_handler = custom_functions_handler; self } - - } /// A parsed [SPARQL query](https://www.w3.org/TR/sparql11-query/) diff --git a/lib/src/sparql/plan.rs b/lib/src/sparql/plan.rs index 2fc31c30..9fd61e2d 100644 --- a/lib/src/sparql/plan.rs +++ b/lib/src/sparql/plan.rs @@ -1,7 +1,7 @@ use crate::model::NamedNode; -use crate::sparql::GraphPattern; -use crate::sparql::model::Variable; use crate::sparql::eval::StringOrStoreString; +use crate::sparql::model::Variable; +use crate::sparql::GraphPattern; use crate::store::numeric_encoder::{ EncodedQuad, EncodedTerm, Encoder, MemoryStrStore, StrContainer, StrLookup, ENCODED_DEFAULT_GRAPH, @@ -307,9 +307,9 @@ pub enum PlanExpression { TimeCast(Box), DateTimeCast(Box), StringCast(Box), - CustomFunction { + CustomFunction { name: NamedNode, - parameters: Vec + parameters: Vec, }, } @@ -420,7 +420,7 @@ impl PlanExpression { e.add_variables(set); } } - PlanExpression::CustomFunction{ parameters, .. } => { + PlanExpression::CustomFunction { parameters, .. } => { for p in parameters { p.add_variables(set); } diff --git a/lib/src/sparql/plan_builder.rs b/lib/src/sparql/plan_builder.rs index abc3ecfb..6e1dcfa0 100644 --- a/lib/src/sparql/plan_builder.rs +++ b/lib/src/sparql/plan_builder.rs @@ -646,7 +646,10 @@ impl PlanBuilder { )? } else { let parameters = self.expression_list(parameters, variables, graph_name)?; - PlanExpression::CustomFunction { name: name.clone(), parameters } + PlanExpression::CustomFunction { + name: name.clone(), + parameters, + } } } }, diff --git a/lib/tests/custom_functions_test_cases.rs b/lib/tests/custom_functions_test_cases.rs index c12eeb0e..9dc07472 100644 --- a/lib/tests/custom_functions_test_cases.rs +++ b/lib/tests/custom_functions_test_cases.rs @@ -6,29 +6,28 @@ use support::*; #[test] fn simple_custom_function_test() { + struct TestHandler; - struct TestHandler; - - impl CustomFunctionsHandler for TestHandler { - fn handle(&self, node: &NamedNode, parameters: &Vec>) -> Option { - let reverse = NamedNode::parse("http://example.com#REVERSE").ok()?; - if *node == reverse { - let param = ¶meters[0]; - if let Some(Term::Literal(literal)) = param { - let value = literal.value(); - let reversed = value.chars().rev().collect::(); - let literal = Literal::new_simple_literal(reversed); - Some(Term::Literal(literal)) - } else { - None + impl CustomFunctionsHandler for TestHandler { + fn handle(&self, node: &NamedNode, parameters: &[Option]) -> Option { + let reverse = NamedNode::parse("http://example.com#REVERSE").ok()?; + if *node == reverse { + let param = ¶meters[0]; + if let Some(Term::Literal(literal)) = param { + let value = literal.value(); + let reversed = value.chars().rev().collect::(); + let literal = Literal::new_simple_literal(reversed); + Some(Term::Literal(literal)) + } else { + None + } + } else { + None + } } - } else { - None - } } - } - let query = r#" + let query = r#" PREFIX ex: PREFIX foaf: SELECT ?name ?reverse @@ -38,28 +37,36 @@ fn simple_custom_function_test() { BIND(ex:REVERSE(?name) as ?reverse) } ORDER BY ?name - "#.to_string(); - + "# + .to_string(); - let options = QueryOptions::default().with_custom_functions_handler(Box::new(TestHandler)); - let triples = br#" + let options = QueryOptions::default().with_custom_functions_handler(Box::new(TestHandler)); + let triples = br#" "Bob" . "Alice" . - "#.as_ref(); - let results = do_query(triples, query, options).unwrap(); - let collected = results.into_values_iter().map(move |b| b.unwrap()).collect::>(); - let solution = vec![ - vec![ Some(literal(String::from("Alice"))), Some(literal(String::from("ecilA"))) ], - vec![ Some(literal(String::from("Bob"))), Some(literal(String::from("boB"))) ], - ]; - assert_eq!(collected, solution); - + "# + .as_ref(); + let results = do_query(triples, query, options).unwrap(); + let collected = results + .into_values_iter() + .map(move |b| b.unwrap()) + .collect::>(); + let solution = vec![ + vec![ + Some(literal(String::from("Alice"))), + Some(literal(String::from("ecilA"))), + ], + vec![ + Some(literal(String::from("Bob"))), + Some(literal(String::from("boB"))), + ], + ]; + assert_eq!(collected, solution); } #[test] fn simple_default_custom_function_test() { - - let query = r#" + let query = r#" PREFIX ex: SELECT ?name ?reverse WHERE @@ -68,20 +75,23 @@ fn simple_default_custom_function_test() { BIND(ex:REVERSE(?name) as ?reverse) } ORDER BY ?name - "#.to_string(); - + "# + .to_string(); - let options = QueryOptions::default(); - let triples = br#" + let options = QueryOptions::default(); + let triples = br#" "Bob" . "Alice" . - "#.as_ref(); - let results = do_query(triples, query, options).unwrap(); - let collected = results.into_values_iter().map(move |b| b.unwrap()).collect::>(); - let solution = vec![ - vec![ Some(literal(String::from("Alice"))), None ], - vec![ Some(literal(String::from("Bob"))), None ], - ]; - assert_eq!(collected, solution); - + "# + .as_ref(); + let results = do_query(triples, query, options).unwrap(); + let collected = results + .into_values_iter() + .map(move |b| b.unwrap()) + .collect::>(); + let solution = vec![ + vec![Some(literal(String::from("Alice"))), None], + vec![Some(literal(String::from("Bob"))), None], + ]; + assert_eq!(collected, solution); } diff --git a/lib/tests/service_test_cases.rs b/lib/tests/service_test_cases.rs index f9596bd5..432a476b 100644 --- a/lib/tests/service_test_cases.rs +++ b/lib/tests/service_test_cases.rs @@ -1,8 +1,6 @@ use failure::format_err; use rudf::model::*; -use rudf::sparql::{ - BindingsIterator, GraphPattern, QueryOptions, ServiceHandler, -}; +use rudf::sparql::{BindingsIterator, GraphPattern, QueryOptions, ServiceHandler}; use rudf::Result; mod support; @@ -216,4 +214,4 @@ fn non_silent_service_test() { "This should have been an error since the service fails" ), } -} \ No newline at end of file +} diff --git a/lib/tests/support/mod.rs b/lib/tests/support/mod.rs index 0de647da..fb88006a 100644 --- a/lib/tests/support/mod.rs +++ b/lib/tests/support/mod.rs @@ -6,7 +6,6 @@ use rudf::sparql::{BindingsIterator, GraphPattern, PreparedQuery, QueryOptions, use rudf::{GraphSyntax, MemoryRepository, Repository, RepositoryConnection, Result}; use std::io::BufRead; - pub(crate) fn ex(id: String) -> Term { Term::NamedNode(NamedNode::parse(format!("http://example.com/{}", &id)).unwrap()) }