use crate::types::sequence::{ItemType, SequenceType};
pub const FN_NAMESPACE: &str = "http://www.w3.org/2005/xpath-functions";
pub const FN_2010_NAMESPACE: &str = "http://www.w3.org/xpath-functions";
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum FunctionArity {
Exact(usize),
Variadic(usize),
Range(usize, usize),
}
impl FunctionArity {
pub fn matches(&self, count: usize) -> bool {
match self {
Self::Exact(n) => count == *n,
Self::Variadic(min) => count >= *min,
Self::Range(min, max) => count >= *min && count <= *max,
}
}
pub fn min_args(&self) -> usize {
match self {
Self::Exact(n) => *n,
Self::Variadic(min) => *min,
Self::Range(min, _) => *min,
}
}
pub fn max_args(&self) -> Option<usize> {
match self {
Self::Exact(n) => Some(*n),
Self::Variadic(_) => None,
Self::Range(_, max) => Some(*max),
}
}
}
#[derive(Debug, Clone)]
pub struct FunctionSignature {
pub namespace: &'static str,
pub local_name: &'static str,
pub arity: FunctionArity,
pub param_types: Vec<SequenceType>,
pub return_type: SequenceType,
}
impl FunctionSignature {
pub fn new(
namespace: &'static str,
local_name: &'static str,
param_types: Vec<SequenceType>,
return_type: SequenceType,
) -> Self {
let arity = FunctionArity::Exact(param_types.len());
Self {
namespace,
local_name,
arity,
param_types,
return_type,
}
}
pub fn variadic(
namespace: &'static str,
local_name: &'static str,
min_args: usize,
param_types: Vec<SequenceType>,
return_type: SequenceType,
) -> Self {
Self {
namespace,
local_name,
arity: FunctionArity::Variadic(min_args),
param_types,
return_type,
}
}
pub fn range(
namespace: &'static str,
local_name: &'static str,
min_args: usize,
max_args: usize,
param_types: Vec<SequenceType>,
return_type: SequenceType,
) -> Self {
Self {
namespace,
local_name,
arity: FunctionArity::Range(min_args, max_args),
param_types,
return_type,
}
}
pub fn matches_arity(&self, count: usize) -> bool {
self.arity.matches(count)
}
pub fn param_type(&self, index: usize) -> SequenceType {
if index < self.param_types.len() {
self.param_types[index].clone()
} else if !self.param_types.is_empty() {
self.param_types.last().unwrap().clone()
} else {
SequenceType::any()
}
}
}
pub fn sig_0(local_name: &'static str, return_type: SequenceType) -> FunctionSignature {
FunctionSignature::new(FN_NAMESPACE, local_name, vec![], return_type)
}
pub fn sig_1(
local_name: &'static str,
arg1: SequenceType,
return_type: SequenceType,
) -> FunctionSignature {
FunctionSignature::new(FN_NAMESPACE, local_name, vec![arg1], return_type)
}
pub fn sig_2(
local_name: &'static str,
arg1: SequenceType,
arg2: SequenceType,
return_type: SequenceType,
) -> FunctionSignature {
FunctionSignature::new(FN_NAMESPACE, local_name, vec![arg1, arg2], return_type)
}
pub fn sig_3(
local_name: &'static str,
arg1: SequenceType,
arg2: SequenceType,
arg3: SequenceType,
return_type: SequenceType,
) -> FunctionSignature {
FunctionSignature::new(
FN_NAMESPACE,
local_name,
vec![arg1, arg2, arg3],
return_type,
)
}
pub mod types {
use super::*;
pub fn string() -> SequenceType {
SequenceType::string()
}
pub fn string_opt() -> SequenceType {
SequenceType::string_optional()
}
pub fn string_star() -> SequenceType {
SequenceType::star(ItemType::AtomicType(crate::types::XmlTypeCode::String))
}
pub fn boolean() -> SequenceType {
SequenceType::boolean()
}
pub fn integer() -> SequenceType {
SequenceType::integer()
}
pub fn integer_opt() -> SequenceType {
SequenceType::integer_optional()
}
pub fn integer_star() -> SequenceType {
SequenceType::star(ItemType::AtomicType(crate::types::XmlTypeCode::Integer))
}
pub fn double() -> SequenceType {
SequenceType::double()
}
pub fn double_opt() -> SequenceType {
SequenceType::double_optional()
}
pub fn any_atomic() -> SequenceType {
SequenceType::any_atomic()
}
pub fn any_atomic_opt() -> SequenceType {
SequenceType::any_atomic_optional()
}
pub fn any_atomic_star() -> SequenceType {
SequenceType::any_atomic_star()
}
pub fn node() -> SequenceType {
SequenceType::node()
}
pub fn node_opt() -> SequenceType {
SequenceType::optional(ItemType::AnyNode)
}
pub fn nodes() -> SequenceType {
SequenceType::nodes()
}
pub fn item() -> SequenceType {
SequenceType::item()
}
pub fn item_opt() -> SequenceType {
SequenceType::optional(ItemType::AnyItem)
}
pub fn any() -> SequenceType {
SequenceType::any()
}
pub fn empty() -> SequenceType {
SequenceType::empty()
}
pub fn qname() -> SequenceType {
SequenceType::qname()
}
pub fn qname_opt() -> SequenceType {
SequenceType::optional(ItemType::AtomicType(crate::types::XmlTypeCode::QName))
}
pub fn any_uri() -> SequenceType {
SequenceType::any_uri()
}
pub fn any_uri_opt() -> SequenceType {
SequenceType::optional(ItemType::AtomicType(crate::types::XmlTypeCode::AnyUri))
}
pub fn datetime() -> SequenceType {
SequenceType::datetime()
}
pub fn datetime_opt() -> SequenceType {
SequenceType::optional(ItemType::AtomicType(crate::types::XmlTypeCode::DateTime))
}
pub fn date() -> SequenceType {
SequenceType::date()
}
pub fn date_opt() -> SequenceType {
SequenceType::optional(ItemType::AtomicType(crate::types::XmlTypeCode::Date))
}
pub fn time() -> SequenceType {
SequenceType::time()
}
pub fn time_opt() -> SequenceType {
SequenceType::optional(ItemType::AtomicType(crate::types::XmlTypeCode::Time))
}
pub fn duration() -> SequenceType {
SequenceType::duration()
}
pub fn duration_opt() -> SequenceType {
SequenceType::optional(ItemType::AtomicType(crate::types::XmlTypeCode::Duration))
}
pub fn day_time_duration() -> SequenceType {
SequenceType::one(ItemType::AtomicType(
crate::types::XmlTypeCode::DayTimeDuration,
))
}
pub fn day_time_duration_opt() -> SequenceType {
SequenceType::optional(ItemType::AtomicType(
crate::types::XmlTypeCode::DayTimeDuration,
))
}
pub fn element() -> SequenceType {
SequenceType::one(ItemType::Element(None, None))
}
pub fn numeric() -> SequenceType {
SequenceType::double()
}
pub fn numeric_opt() -> SequenceType {
SequenceType::double_optional()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_arity_exact() {
let arity = FunctionArity::Exact(2);
assert!(arity.matches(2));
assert!(!arity.matches(1));
assert!(!arity.matches(3));
assert_eq!(arity.min_args(), 2);
assert_eq!(arity.max_args(), Some(2));
}
#[test]
fn test_arity_variadic() {
let arity = FunctionArity::Variadic(2);
assert!(!arity.matches(1));
assert!(arity.matches(2));
assert!(arity.matches(10));
assert_eq!(arity.min_args(), 2);
assert_eq!(arity.max_args(), None);
}
#[test]
fn test_arity_range() {
let arity = FunctionArity::Range(1, 3);
assert!(!arity.matches(0));
assert!(arity.matches(1));
assert!(arity.matches(2));
assert!(arity.matches(3));
assert!(!arity.matches(4));
assert_eq!(arity.min_args(), 1);
assert_eq!(arity.max_args(), Some(3));
}
#[test]
fn test_signature_param_type() {
let sig = sig_2(
"substring",
types::string(),
types::double(),
types::string(),
);
assert_eq!(sig.param_type(0), types::string());
assert_eq!(sig.param_type(1), types::double());
assert_eq!(sig.param_type(2), types::double());
}
#[test]
fn test_signature_matches_arity() {
let sig = sig_2("test", types::string(), types::string(), types::string());
assert!(!sig.matches_arity(1));
assert!(sig.matches_arity(2));
assert!(!sig.matches_arity(3));
}
}