#![allow(clippy::result_large_err)]
use std::collections::HashMap;
use std::rc::Rc;
use qlexpress::aparser::import_manager::QLImport;
use qlexpress::default_class_supplier::DefaultClassSupplier;
use qlexpress::init_options::InitOptions;
use qlexpress::ql_options::QLOptions;
use qlexpress::runtime::native_type::NativeType;
use qlexpress::runtime::value::DataValue;
use qlexpress::security::ql_security_strategy::QLSecurityStrategy;
use qlexpress::Express4Runner;
fn runner_with(calc: NativeType) -> Express4Runner {
let mut supplier = DefaultClassSupplier::instance();
supplier.register("com.example.Calc");
let mut runner = Express4Runner::with_init_options(
InitOptions::builder()
.class_supplier(Rc::new(supplier))
.add_default_import(vec![QLImport::import_cls("com.example.Calc")])
.security_strategy(QLSecurityStrategy::open())
.build(),
);
runner.register_native_type(calc);
runner
}
fn opts() -> QLOptions {
QLOptions::builder().build()
}
fn calc_with_constructor(
ctor: impl Fn(&[DataValue]) -> Result<DataValue, qlexpress::exception::QLException> + 'static,
) -> NativeType {
let mut calc = NativeType::named("com.example.Calc");
calc.constructor = Some(Rc::new(ctor));
calc
}
#[test]
fn new_instance_with_1arg_constructor() {
let runner = runner_with(calc_with_constructor(|args| {
match args.first() {
Some(DataValue::Int(x)) => Ok(DataValue::string(format!("Calc({x})"))),
_ => Ok(DataValue::Null),
}
}));
let r = runner
.execute("new Calc(7)", HashMap::new(), &opts())
.expect("ok")
.into_result();
assert_eq!(r, DataValue::string("Calc(7)"));
}
#[test]
fn new_instance_with_2arg_constructor() {
let runner = runner_with(calc_with_constructor(|args| {
if args.len() != 2 {
return Err(qlexpress::exception::QLException::for_test(
qlexpress::exception::ql_exception::QLExceptionKind::Syntax,
format!("expected 2 args, got {}", args.len()),
qlexpress::exception::error_codes::INVALID_NUMBER,
));
}
let a = qlexpress::runtime::data::convert::to_i64(&args[0]);
let b = qlexpress::runtime::data::convert::to_i64(&args[1]);
Ok(DataValue::Long(a + b))
}));
let r = runner
.execute("new Calc(3, 4)", HashMap::new(), &opts())
.expect("ok")
.into_result();
assert_eq!(r, DataValue::Long(7));
}
#[test]
fn new_instance_with_array_arg() {
let runner = runner_with(calc_with_constructor(|args| {
if let Some(DataValue::List(_)) = args.first() {
Ok(DataValue::Str("ok".into()))
} else {
Ok(DataValue::Str("not-list".into()))
}
}));
let r = runner
.execute("new Calc([1, 2, 3])", HashMap::new(), &opts())
.expect("ok")
.into_result();
assert_eq!(r, DataValue::Str("ok".into()));
}
#[test]
fn new_instance_no_constructor_returns_error() {
let calc = NativeType::named("com.example.Calc");
let runner = runner_with(calc);
let r = runner.execute("new Calc(1)", HashMap::new(), &opts());
assert!(r.is_err());
}
#[test]
fn new_instance_with_string_arg() {
let runner = runner_with(calc_with_constructor(|args| match args.first() {
Some(DataValue::Str(s)) => Ok(DataValue::string(format!("Calc({s})"))),
_ => Ok(DataValue::Null),
}));
let r = runner
.execute("new Calc(\"hello\")", HashMap::new(), &opts())
.expect("ok")
.into_result();
assert_eq!(r, DataValue::string("Calc(hello)"));
}
#[test]
fn new_instance_returns_data_value_for_use() {
let runner = runner_with(calc_with_constructor(|args| {
let x = qlexpress::runtime::data::convert::to_i64(&args[0]);
Ok(DataValue::Long(x * 2))
}));
let r = runner
.execute("new Calc(5) + 3", HashMap::new(), &opts())
.expect("ok")
.into_result();
assert_eq!(r, DataValue::Long(13));
}