use crate::extensions::{
AggregateState, CustomAggregate, CustomFunction, ExecutionContext, Value, ValueType,
};
use anyhow::{bail, Result};
use chrono::Datelike;
#[derive(Debug, Clone)]
pub(crate) struct NowFunction;
impl CustomFunction for NowFunction {
fn name(&self) -> &str {
"http://www.w3.org/2005/xpath-functions#current-dateTime"
}
fn arity(&self) -> Option<usize> {
Some(0)
}
fn parameter_types(&self) -> Vec<ValueType> {
vec![]
}
fn return_type(&self) -> ValueType {
ValueType::DateTime
}
fn documentation(&self) -> &str {
"Returns the current date and time"
}
fn clone_function(&self) -> Box<dyn CustomFunction> {
Box::new(self.clone())
}
fn execute(&self, args: &[Value], context: &ExecutionContext) -> Result<Value> {
if !args.is_empty() {
bail!("now() takes no arguments");
}
Ok(Value::DateTime(context.query_time))
}
}
#[derive(Debug, Clone)]
pub(crate) struct YearFunction;
impl CustomFunction for YearFunction {
fn name(&self) -> &str {
"http://www.w3.org/2005/xpath-functions#year-from-dateTime"
}
fn arity(&self) -> Option<usize> {
Some(1)
}
fn parameter_types(&self) -> Vec<ValueType> {
vec![ValueType::DateTime]
}
fn return_type(&self) -> ValueType {
ValueType::Integer
}
fn documentation(&self) -> &str {
"Extracts the year from a dateTime"
}
fn clone_function(&self) -> Box<dyn CustomFunction> {
Box::new(self.clone())
}
fn execute(&self, args: &[Value], _context: &ExecutionContext) -> Result<Value> {
if args.len() != 1 {
bail!("year() requires exactly 1 argument");
}
match &args[0] {
Value::DateTime(dt) => Ok(Value::Integer(dt.year() as i64)),
_ => bail!("year() requires a dateTime argument"),
}
}
}
macro_rules! stub_function {
($name:ident, $iri:expr_2021, $arity:expr_2021, $doc:expr_2021) => {
#[derive(Debug, Clone)]
pub(crate) struct $name;
impl CustomFunction for $name {
fn name(&self) -> &str {
$iri
}
fn arity(&self) -> Option<usize> {
$arity
}
fn parameter_types(&self) -> Vec<ValueType> {
vec![]
}
fn return_type(&self) -> ValueType {
ValueType::String
}
fn documentation(&self) -> &str {
$doc
}
fn clone_function(&self) -> Box<dyn CustomFunction> {
Box::new(self.clone())
}
fn execute(&self, _args: &[Value], _context: &ExecutionContext) -> Result<Value> {
bail!("Function {} not yet implemented", self.name())
}
}
};
}
stub_function!(
MonthFunction,
"http://www.w3.org/2005/xpath-functions#month-from-dateTime",
Some(1),
"Extracts month from dateTime"
);
stub_function!(
DayFunction,
"http://www.w3.org/2005/xpath-functions#day-from-dateTime",
Some(1),
"Extracts day from dateTime"
);
stub_function!(
HoursFunction,
"http://www.w3.org/2005/xpath-functions#hours-from-dateTime",
Some(1),
"Extracts hours from dateTime"
);
stub_function!(
MinutesFunction,
"http://www.w3.org/2005/xpath-functions#minutes-from-dateTime",
Some(1),
"Extracts minutes from dateTime"
);
stub_function!(
SecondsFunction,
"http://www.w3.org/2005/xpath-functions#seconds-from-dateTime",
Some(1),
"Extracts seconds from dateTime"
);
stub_function!(
TimezoneFunction,
"http://www.w3.org/2005/xpath-functions#timezone-from-dateTime",
Some(1),
"Extracts timezone from dateTime"
);
stub_function!(
TzFunction,
"http://www.w3.org/2005/xpath-functions#tz",
Some(1),
"Returns timezone string"
);
#[derive(Debug, Clone)]
pub(crate) struct Md5Function;
impl CustomFunction for Md5Function {
fn name(&self) -> &str {
"http://www.w3.org/2005/xpath-functions#md5"
}
fn arity(&self) -> Option<usize> {
Some(1)
}
fn parameter_types(&self) -> Vec<ValueType> {
vec![ValueType::String]
}
fn return_type(&self) -> ValueType {
ValueType::String
}
fn documentation(&self) -> &str {
"Computes MD5 hash of a string"
}
fn clone_function(&self) -> Box<dyn CustomFunction> {
Box::new(self.clone())
}
fn execute(&self, args: &[Value], _context: &ExecutionContext) -> Result<Value> {
if args.len() != 1 {
bail!("md5() requires exactly 1 argument");
}
let s = match &args[0] {
Value::String(s) => s,
Value::Literal { value, .. } => value,
_ => bail!("md5() requires a string argument"),
};
Ok(Value::String(format!("md5:{len}", len = s.len())))
}
}
stub_function!(
Sha1Function,
"http://www.w3.org/2005/xpath-functions#sha1",
Some(1),
"Computes SHA1 hash"
);
stub_function!(
Sha256Function,
"http://www.w3.org/2005/xpath-functions#sha256",
Some(1),
"Computes SHA256 hash"
);
stub_function!(
Sha384Function,
"http://www.w3.org/2005/xpath-functions#sha384",
Some(1),
"Computes SHA384 hash"
);
stub_function!(
Sha512Function,
"http://www.w3.org/2005/xpath-functions#sha512",
Some(1),
"Computes SHA512 hash"
);
#[derive(Debug, Clone)]
pub(crate) struct CountAggregate;
impl CustomAggregate for CountAggregate {
fn name(&self) -> &str {
"http://www.w3.org/2005/xpath-functions#count"
}
fn documentation(&self) -> &str {
"Counts the number of values"
}
fn init(&self) -> Box<dyn AggregateState> {
Box::new(CountState { count: 0 })
}
}
#[derive(Debug, Clone)]
struct CountState {
count: i64,
}
impl AggregateState for CountState {
fn add(&mut self, _value: &Value) -> Result<()> {
self.count += 1;
Ok(())
}
fn result(&self) -> Result<Value> {
Ok(Value::Integer(self.count))
}
fn reset(&mut self) {
self.count = 0;
}
fn clone_state(&self) -> Box<dyn AggregateState> {
Box::new(self.clone())
}
}
#[derive(Debug, Clone)]
pub(crate) struct SumAggregate;
impl CustomAggregate for SumAggregate {
fn name(&self) -> &str {
"http://www.w3.org/2005/xpath-functions#sum"
}
fn documentation(&self) -> &str {
"Sums numeric values"
}
fn init(&self) -> Box<dyn AggregateState> {
Box::new(SumState { sum: 0.0, count: 0 })
}
}
#[derive(Debug, Clone)]
struct SumState {
sum: f64,
count: usize,
}
impl AggregateState for SumState {
fn add(&mut self, value: &Value) -> Result<()> {
match value {
Value::Integer(i) => {
self.sum += *i as f64;
self.count += 1;
}
Value::Float(f) => {
self.sum += f;
self.count += 1;
}
_ => bail!("sum() can only aggregate numeric values"),
}
Ok(())
}
fn result(&self) -> Result<Value> {
if self.count == 0 {
Ok(Value::Integer(0))
} else {
Ok(Value::Float(self.sum))
}
}
fn reset(&mut self) {
self.sum = 0.0;
self.count = 0;
}
fn clone_state(&self) -> Box<dyn AggregateState> {
Box::new(self.clone())
}
}
#[derive(Debug, Clone)]
pub(crate) struct StubAggregateState;
impl AggregateState for StubAggregateState {
fn add(&mut self, _value: &Value) -> Result<()> {
Ok(())
}
fn result(&self) -> Result<Value> {
Ok(Value::Null)
}
fn reset(&mut self) {}
fn clone_state(&self) -> Box<dyn AggregateState> {
Box::new(StubAggregateState)
}
}
macro_rules! stub_aggregate {
($name:ident, $iri:expr_2021, $doc:expr_2021) => {
#[derive(Debug, Clone)]
pub(crate) struct $name;
impl CustomAggregate for $name {
fn name(&self) -> &str {
$iri
}
fn documentation(&self) -> &str {
$doc
}
fn init(&self) -> Box<dyn AggregateState> {
Box::new(StubAggregateState)
}
}
};
}
stub_aggregate!(
AvgAggregate,
"http://www.w3.org/2005/xpath-functions#avg",
"Computes average of numeric values"
);
stub_aggregate!(
MinAggregate,
"http://www.w3.org/2005/xpath-functions#min",
"Finds minimum value"
);
stub_aggregate!(
MaxAggregate,
"http://www.w3.org/2005/xpath-functions#max",
"Finds maximum value"
);
stub_aggregate!(
GroupConcatAggregate,
"http://www.w3.org/2005/xpath-functions#group-concat",
"Concatenates group values"
);
stub_aggregate!(
SampleAggregate,
"http://www.w3.org/2005/xpath-functions#sample",
"Returns sample value from group"
);