use num_traits::ToPrimitive;
use crate::spec::function::Function;
use crate::spec::function::SPathType;
use crate::spec::function::SPathValue;
use crate::spec::function::ValueType;
use crate::ConcreteVariantArray;
use crate::ConcreteVariantObject;
use crate::Literal;
use crate::VariantValue;
pub fn length<T: VariantValue>() -> Function<T> {
Function::new(
"length",
vec![SPathType::Value],
SPathType::Value,
Box::new(move |mut args| {
assert_eq!(args.len(), 1);
fn value_len<T: VariantValue>(t: &T) -> Option<usize> {
if let Some(s) = t.as_str() {
Some(s.chars().count())
} else if let Some(a) = t.as_array() {
Some(a.len())
} else {
t.as_object().map(|o| o.len())
}
}
let value = args.pop().unwrap().into_value().unwrap();
let len = match value {
ValueType::Value(v) => value_len(&v),
ValueType::Node(v) => value_len(v),
ValueType::Nothing => None,
}
.and_then(|l| l.to_i64())
.and_then(|len| T::from_literal(Literal::Int(len)));
match len {
Some(v) => SPathValue::Value(v),
None => SPathValue::Nothing,
}
}),
)
}
pub fn count<T: VariantValue>() -> Function<T> {
Function::new(
"count",
vec![SPathType::Nodes],
SPathType::Value,
Box::new(move |mut args| {
assert_eq!(args.len(), 1);
let nodes = args.pop().unwrap().into_nodes().unwrap();
let len = nodes
.len()
.to_i64()
.and_then(|len| T::from_literal(Literal::Int(len)));
match len {
Some(v) => SPathValue::Value(v),
None => SPathValue::Nothing,
}
}),
)
}
pub fn value<T: VariantValue>() -> Function<T> {
Function::new(
"value",
vec![SPathType::Nodes],
SPathType::Value,
Box::new(move |mut args| {
assert_eq!(args.len(), 1);
let value = args.pop().unwrap().into_nodes().unwrap();
if value.len() > 1 {
SPathValue::Nothing
} else {
match value.first() {
Some(v) => SPathValue::Node(v),
None => SPathValue::Nothing,
}
}
}),
)
}
#[cfg(feature = "regex")]
pub fn matches<T: VariantValue>() -> Function<T> {
Function::new(
"match",
vec![SPathType::Value, SPathType::Value],
SPathType::Logical,
Box::new(move |mut args| {
assert_eq!(args.len(), 2);
let matcher = args.pop().unwrap().into_value().unwrap();
let expr = args.pop().unwrap().into_value().unwrap();
let matches = match (
matcher.as_value().and_then(|v| v.as_str()),
expr.as_value().and_then(|v| v.as_str()),
) {
(Some(r), Some(s)) => regex::Regex::new(format!("(?R)^({r})$").as_str())
.map(|r| r.is_match(s))
.unwrap_or_default(),
_ => false,
};
SPathValue::Logical(matches.into())
}),
)
}
#[cfg(feature = "regex")]
pub fn search<T: VariantValue>() -> Function<T> {
Function::new(
"search",
vec![SPathType::Value, SPathType::Value],
SPathType::Logical,
Box::new(move |mut args| {
assert_eq!(args.len(), 2);
let matcher = args.pop().unwrap().into_value().unwrap();
let expr = args.pop().unwrap().into_value().unwrap();
let matches = match (
matcher.as_value().and_then(|v| v.as_str()),
expr.as_value().and_then(|v| v.as_str()),
) {
(Some(r), Some(s)) => regex::Regex::new(format!("(?R)({r})").as_str())
.map(|r| r.is_match(s))
.unwrap_or_default(),
_ => false,
};
SPathValue::Logical(matches.into())
}),
)
}