use openjd_expr::error::ExpressionError;
use openjd_expr::function_library::EvalContext;
use openjd_expr::functions::misc::*;
use openjd_expr::types::ExprType;
use openjd_expr::{ExprValue, ParsedExpression, PathFormat, RangeExpr, SymbolTable};
fn eval(expr: &str) -> ExprValue {
ParsedExpression::new(expr)
.and_then(|p| p.evaluate(&SymbolTable::new()))
.unwrap()
}
fn eval_with(expr: &str, st: &SymbolTable) -> ExprValue {
ParsedExpression::new(expr)
.and_then(|p| p.evaluate(st))
.unwrap()
}
fn eval_posix(expr: &str) -> ExprValue {
let parsed = openjd_expr::ParsedExpression::new(expr).unwrap();
let st = SymbolTable::new();
let symtabs = [&st];
parsed
.with_path_format(PathFormat::Posix)
.evaluate(&symtabs)
.unwrap()
}
fn eval_posix_st(expr: &str, st: &SymbolTable) -> ExprValue {
let parsed = openjd_expr::ParsedExpression::new(expr).unwrap();
let symtabs = [st];
parsed
.with_path_format(PathFormat::Posix)
.evaluate(&symtabs)
.unwrap()
}
fn assert_err(expr: &str, expected: &[&str]) {
let e = ParsedExpression::new(expr)
.and_then(|p| p.evaluate(&SymbolTable::new()))
.unwrap_err()
.to_string();
let joined = expected.concat();
assert!(e.contains(&joined), "got:\n{e}\nexpected:\n{joined}");
}
struct Ctx;
impl EvalContext for Ctx {
fn path_format(&self) -> PathFormat {
PathFormat::Posix
}
fn count_op(&mut self) -> Result<(), ExpressionError> {
Ok(())
}
fn count_ops(&mut self, _: usize) -> Result<(), ExpressionError> {
Ok(())
}
fn count_string_ops(&mut self, _: usize) -> Result<(), ExpressionError> {
Ok(())
}
fn check_memory(&self, _: usize) -> Result<(), ExpressionError> {
Ok(())
}
}
#[test]
fn fail_with_message() {
assert_err(
"fail('error message')",
&["error message\n", " fail('error message')\n"],
);
}
#[test]
fn fail_no_args() {
assert_err(
"fail()",
&[
"fail() takes 1 argument(s), but 0 were given\n",
" fail()\n",
],
);
}
#[test]
fn fail_fn_direct_no_args() {
let err = fail_fn(&mut Ctx, &[]).unwrap_err();
assert!(err.to_string().contains("fail() called"));
}
#[test]
fn zfill_string() {
assert_eq!(eval("zfill('42', 5)").to_display_string(), "00042");
}
#[test]
fn zfill_negative() {
assert_eq!(eval("zfill('-42', 6)").to_display_string(), "-00042");
}
#[test]
fn zfill_int_input() {
assert_eq!(eval("zfill(42, 5)").to_display_string(), "00042");
}
#[test]
fn zfill_already_wide() {
assert_eq!(eval("zfill('12345', 3)").to_display_string(), "12345");
}
#[test]
fn zfill_plus_sign() {
assert_eq!(eval("zfill('+7', 5)").to_display_string(), "+0007");
}
#[test]
fn any_with_true() {
assert_eq!(eval("any([True, False])").to_display_string(), "true");
}
#[test]
fn any_all_false() {
assert_eq!(eval("any([False, False])").to_display_string(), "false");
}
#[test]
fn any_empty() {
assert_eq!(eval("any([])").to_display_string(), "false");
}
#[test]
fn any_all_true() {
assert_eq!(eval("any([True, True])").to_display_string(), "true");
}
#[test]
fn all_with_true() {
assert_eq!(eval("all([True, True])").to_display_string(), "true");
}
#[test]
fn all_with_false() {
assert_eq!(eval("all([True, False])").to_display_string(), "false");
}
#[test]
fn all_empty() {
assert_eq!(eval("all([])").to_display_string(), "true");
}
#[test]
fn all_all_false() {
assert_eq!(eval("all([False, False])").to_display_string(), "false");
}
#[test]
fn abs_neg_int() {
assert_eq!(eval("abs(-5)").to_display_string(), "5");
}
#[test]
fn abs_pos_int() {
assert_eq!(eval("abs(5)").to_display_string(), "5");
}
#[test]
fn abs_zero() {
assert_eq!(eval("abs(0)").to_display_string(), "0");
}
#[test]
fn abs_neg_float() {
assert_eq!(eval("abs(-1.5)").to_display_string(), "1.5");
}
#[test]
fn abs_pos_float() {
assert_eq!(eval("abs(1.5)").to_display_string(), "1.5");
}
#[test]
fn len_string_basic() {
assert_eq!(eval("len('hello')").to_display_string(), "5");
}
#[test]
fn len_string_empty() {
assert_eq!(eval("len('')").to_display_string(), "0");
}
#[test]
fn len_path_basic() {
let mut st = SymbolTable::new();
st.set("p", ExprValue::new_path("/foo/bar", PathFormat::Posix))
.unwrap();
assert_eq!(eval_posix_st("len(p)", &st).to_display_string(), "8");
}
#[test]
fn len_path_direct() {
let r = len_path(&mut Ctx, &[ExprValue::new_path("/x", PathFormat::Posix)]).unwrap();
assert_eq!(r, ExprValue::Int(2));
}
#[test]
fn len_list_basic() {
assert_eq!(eval("len([1, 2, 3])").to_display_string(), "3");
}
#[test]
fn len_list_empty() {
assert_eq!(eval("len([])").to_display_string(), "0");
}
#[test]
fn len_range_basic() {
let mut st = SymbolTable::new();
st.set(
"r",
ExprValue::RangeExpr("1-10".parse::<RangeExpr>().unwrap()),
)
.unwrap();
assert_eq!(eval_with("len(r)", &st).to_display_string(), "10");
}
#[test]
fn path_from_string() {
assert!(matches!(eval("path('/tmp/file')"), ExprValue::Path { .. }));
}
#[test]
fn path_from_path() {
let mut st = SymbolTable::new();
st.set("p", ExprValue::new_path("/foo", PathFormat::Posix))
.unwrap();
assert!(matches!(
eval_posix_st("path(p)", &st),
ExprValue::Path { .. }
));
}
#[test]
fn path_from_list() {
assert_eq!(
eval_posix("path(['/a', 'b', 'c'])").to_display_string(),
"/a/b/c"
);
}
#[test]
fn path_from_list_with_non_string() {
let result = ExprValue::make_list(
vec![ExprValue::from("/a"), ExprValue::Int(42)],
ExprType::union(vec![ExprType::STRING, ExprType::INT]),
);
assert!(result.is_err());
}
#[test]
fn path_fn_unsupported_type() {
let err = path_fn(&mut Ctx, &[ExprValue::Bool(true)]).unwrap_err();
assert!(err.to_string().contains("path() not supported for"));
}