use crate::error::XmlError;
use crate::xpath::eval::{Numeric, Value};
use crate::xpath::index::DocIndexLike;
pub mod math;
pub mod date;
pub mod str;
pub mod sets;
pub mod regexp;
pub mod common;
type Result<T> = std::result::Result<T, XmlError>;
pub const MATH_NS: &str = "http://exslt.org/math";
pub const DATE_NS: &str = "http://exslt.org/dates-and-times";
pub const STR_NS: &str = "http://exslt.org/strings";
pub const SET_NS: &str = "http://exslt.org/sets";
pub const REGEXP_NS: &str = "http://exslt.org/regular-expressions";
pub const COMMON_NS: &str = "http://exslt.org/common";
pub const DYN_NS: &str = "http://exslt.org/dynamic";
pub const XPATH_MATH_NS: &str = "http://www.w3.org/2005/xpath-functions/math";
pub fn dispatch<I: DocIndexLike>(
ns_uri: &str,
name: &str,
args: Vec<Value>,
idx: &I,
) -> Option<Result<Value>> {
match ns_uri {
MATH_NS => math::dispatch(name, args, idx),
DATE_NS => date::dispatch(name, args, idx),
STR_NS => self::str::dispatch(name, args, idx),
SET_NS => sets::dispatch(name, args, idx),
REGEXP_NS => regexp::dispatch(name, args, idx),
COMMON_NS => common::dispatch(name, args, idx),
XPATH_MATH_NS => xpath_math_dispatch(name, args, idx),
_ => None,
}
}
fn xpath_math_dispatch<I: DocIndexLike>(
name: &str, args: Vec<Value>, idx: &I,
) -> Option<Result<Value>> {
use crate::error::{ErrorDomain, ErrorLevel};
let err = |msg: &str| XmlError::new(ErrorDomain::XPath, ErrorLevel::Error, msg.to_string());
fn num<I: DocIndexLike>(v: &Value, idx: &I) -> f64 {
crate::xpath::eval::value_to_number(v, idx)
}
let r = match name {
"pi" => {
if !args.is_empty() { return Some(Err(err("math:pi() takes no arguments"))); }
Ok(Value::Number(Numeric::Double(std::f64::consts::PI)))
}
"exp" | "exp10" | "log" | "log10"
| "sqrt" | "sin" | "cos" | "tan"
| "asin" | "acos" | "atan" => {
if args.len() != 1 {
return Some(Err(err(
&format!("math:{name}() takes 1 argument"))));
}
let n = num(&args[0], idx);
let r = match name {
"exp" => n.exp(),
"exp10" => 10_f64.powf(n),
"log" => n.ln(),
"log10" => n.log10(),
"sqrt" => n.sqrt(),
"sin" => n.sin(),
"cos" => n.cos(),
"tan" => n.tan(),
"asin" => n.asin(),
"acos" => n.acos(),
"atan" => n.atan(),
_ => unreachable!(),
};
Ok(Value::Number(Numeric::Double(r)))
}
"pow" => {
if args.len() != 2 {
return Some(Err(err("math:pow() takes 2 arguments")));
}
let x = num(&args[0], idx);
let y = num(&args[1], idx);
Ok(Value::Number(Numeric::Double(x.powf(y))))
}
"atan2" => {
if args.len() != 2 {
return Some(Err(err("math:atan2() takes 2 arguments")));
}
let y = num(&args[0], idx);
let x = num(&args[1], idx);
Ok(Value::Number(Numeric::Double(y.atan2(x))))
}
_ => return None,
};
Some(r)
}