use itertools::{Itertools, Position};
use maybe_sync::Rc;
use crate::rendertime::WRONG_TYPE_MESSAGE;
use crate::{Function, Type, Value};
struct Len;
impl Function for Len {
fn accepts(&self, args: Vec<Type>) -> Option<Type> {
if args.len() != 1 {
return None;
}
matches!(args[0], Type::Array(_)).then_some(Type::Int)
}
fn run(
&self,
mut args: Vec<Value>,
) -> Result<Value, Box<maybe_sync::dyn_maybe_send_sync!(std::error::Error)>> {
let Some(Value::ArrayOrTuple(inner)) = args.pop() else {
panic!("wrong input type for builtin function len; {WRONG_TYPE_MESSAGE}");
};
Ok(Value::Int(
inner
.len()
.try_into()
.expect("array size too large in builtin function len"),
))
}
}
struct Join;
impl Function for Join {
fn accepts(&self, mut args: Vec<Type>) -> Option<Type> {
if args.len() != 2 {
return None;
}
let (Some(Type::Array(to_join_ty)), Some(Some(joiner_ty))) =
(args.pop(), args.pop().map(|ty| ty.to_printable()))
else {
return None;
};
let Some(to_join_ty) = to_join_ty.as_deref().and_then(Type::to_printable).flatten() else {
let Some(joiner_ty) = joiner_ty else {
return None;
};
return Some(Type::Text(joiner_ty));
};
joiner_ty
.is_none_or(|joiner_ty| to_join_ty == joiner_ty)
.then_some(Type::Text(to_join_ty))
}
fn run(
&self,
mut args: Vec<Value>,
) -> Result<Value, Box<maybe_sync::dyn_maybe_send_sync!(std::error::Error)>> {
let (Some(Value::ArrayOrTuple(values)), Some(Ok(joiner))): (_, Option<Result<String, _>>) =
(args.pop(), args.pop().map(Value::try_into))
else {
panic!("bad arguments ({args:?}) for builtin function join; {WRONG_TYPE_MESSAGE}");
};
let mut result = String::new();
for (pos, value) in values.into_iter().with_position() {
let Ok(str): Result<String, _> = value.clone().try_into() else {
panic!("non printable value ({value:?}) in array in builtin function join; {WRONG_TYPE_MESSAGE}");
};
result.push_str(&str);
if !matches!(pos, Position::Only | Position::Last) {
result.push_str(&joiner);
}
}
Ok(Value::Text(result))
}
}
struct ToUnsafe;
impl Function for ToUnsafe {
fn accepts(&self, mut args: Vec<Type>) -> Option<Type> {
if args.len() != 1 {
return None;
}
args.pop()
.unwrap()
.to_printable()
.is_some()
.then_some(Type::Text("unsafe".to_string()))
}
fn run(
&self,
mut args: Vec<Value>,
) -> Result<Value, Box<maybe_sync::dyn_maybe_send_sync!(std::error::Error)>> {
let out: String = args
.pop()
.unwrap_or_else(|| {
panic!(
"bad arguments ({args:?}) for builtin function toUnsafe; {WRONG_TYPE_MESSAGE}"
)
})
.try_into()
.unwrap_or_else(|_| {
panic!("wrong input type for builtin function toUnsafe, {WRONG_TYPE_MESSAGE}")
});
Ok(Value::Text(out))
}
}
#[must_use]
pub fn functions() -> Vec<(&'static str, Rc<maybe_sync::dyn_maybe_send_sync!(Function)>)> {
vec![
("len", Rc::new(Len)),
("join", Rc::new(Join)),
("toUnsafe", Rc::new(ToUnsafe)),
]
}