use std::sync::Arc;
use crate::error::Error;
use crate::utils::SealedMarker;
use crate::value::{ArgType, FunctionArgs, Value};
use crate::vm::State;
type TestFunc = dyn Fn(&State, &[Value]) -> Result<bool, Error> + Sync + Send + 'static;
#[derive(Clone)]
pub(crate) struct BoxedTest(Arc<TestFunc>);
pub trait TestResult {
#[doc(hidden)]
fn into_result(self) -> Result<bool, Error>;
}
impl TestResult for Result<bool, Error> {
fn into_result(self) -> Result<bool, Error> {
self
}
}
impl TestResult for bool {
fn into_result(self) -> Result<bool, Error> {
Ok(self)
}
}
pub trait Test<Rv, Args>: Send + Sync + 'static {
#[doc(hidden)]
fn perform(&self, args: Args, _: SealedMarker) -> Rv;
}
macro_rules! tuple_impls {
( $( $name:ident )* ) => {
impl<Func, Rv, $($name),*> Test<Rv, ($($name,)*)> for Func
where
Func: Fn($($name),*) -> Rv + Send + Sync + 'static,
Rv: TestResult,
$($name: for<'a> ArgType<'a>),*
{
fn perform(&self, args: ($($name,)*), _: SealedMarker) -> Rv {
#[allow(non_snake_case)]
let ($($name,)*) = args;
(self)($($name,)*)
}
}
};
}
tuple_impls! {}
tuple_impls! { A }
tuple_impls! { A B }
tuple_impls! { A B C }
tuple_impls! { A B C D }
tuple_impls! { A B C D E }
impl BoxedTest {
pub fn new<F, Rv, Args>(f: F) -> BoxedTest
where
F: Test<Rv, Args> + for<'a> Test<Rv, <Args as FunctionArgs<'a>>::Output>,
Rv: TestResult,
Args: for<'a> FunctionArgs<'a>,
{
BoxedTest(Arc::new(move |state, args| -> Result<bool, Error> {
f.perform(ok!(Args::from_values(Some(state), args)), SealedMarker)
.into_result()
}))
}
pub fn perform(&self, state: &State, args: &[Value]) -> Result<bool, Error> {
(self.0)(state, args)
}
}
pub fn is_undefined(v: Value) -> bool {
v.is_undefined()
}
pub fn is_defined(v: Value) -> bool {
!v.is_undefined()
}
pub fn is_none(v: Value) -> bool {
v.is_none()
}
pub fn is_safe(v: Value) -> bool {
v.is_safe()
}
#[cfg(feature = "builtins")]
mod builtins {
use super::*;
use std::borrow::Cow;
use std::convert::TryFrom;
use crate::value::ValueKind;
#[cfg_attr(docsrs, doc(cfg(feature = "builtins")))]
pub fn is_odd(v: Value) -> bool {
i128::try_from(v).ok().map_or(false, |x| x % 2 != 0)
}
#[cfg_attr(docsrs, doc(cfg(feature = "builtins")))]
pub fn is_even(v: Value) -> bool {
i128::try_from(v).ok().map_or(false, |x| x % 2 == 0)
}
#[cfg_attr(docsrs, doc(cfg(feature = "builtins")))]
pub fn is_number(v: Value) -> bool {
matches!(v.kind(), ValueKind::Number)
}
#[cfg_attr(docsrs, doc(cfg(feature = "builtins")))]
pub fn is_string(v: Value) -> bool {
matches!(v.kind(), ValueKind::String)
}
#[cfg_attr(docsrs, doc(cfg(feature = "builtins")))]
pub fn is_sequence(v: Value) -> bool {
matches!(v.kind(), ValueKind::Seq)
}
#[cfg_attr(docsrs, doc(cfg(feature = "builtins")))]
pub fn is_mapping(v: Value) -> bool {
matches!(v.kind(), ValueKind::Map)
}
#[cfg_attr(docsrs, doc(cfg(feature = "builtins")))]
pub fn is_startingwith(v: Cow<'_, str>, other: Cow<'_, str>) -> bool {
v.starts_with(&other as &str)
}
#[cfg_attr(docsrs, doc(cfg(feature = "builtins")))]
pub fn is_endingwith(v: Cow<'_, str>, other: Cow<'_, str>) -> bool {
v.ends_with(&other as &str)
}
#[test]
fn test_basics() {
fn test(_: &State, a: u32, b: u32) -> bool {
assert_eq!(a, 23);
a == b
}
let env = crate::Environment::new();
State::with_dummy(&env, |state| {
let bx = BoxedTest::new(test);
assert!(bx
.perform(state, &[Value::from(23), Value::from(23)][..])
.unwrap());
});
}
}
#[cfg(feature = "builtins")]
pub use self::builtins::*;