use crate::utils::state::{get_handles_sub_state, put_handle};
use duckscript::runner;
use duckscript::types::command::{Command, CommandResult, Commands};
use duckscript::types::error::ScriptError;
use duckscript::types::instruction::Instruction;
use duckscript::types::runtime::{Context, StateValue};
use std::collections::HashMap;
#[derive(Clone)]
pub(crate) struct ErrorCommand {}
impl Command for ErrorCommand {
fn name(&self) -> String {
"test_error".to_string()
}
fn clone_and_box(&self) -> Box<dyn Command> {
Box::new((*self).clone())
}
fn run(&self, _arguments: Vec<String>) -> CommandResult {
CommandResult::Error("test".to_string())
}
}
#[derive(Clone)]
pub(crate) struct SetCommand {}
impl Command for SetCommand {
fn name(&self) -> String {
"test_set".to_string()
}
fn help(&self) -> String {
"".to_string()
}
fn clone_and_box(&self) -> Box<dyn Command> {
Box::new((*self).clone())
}
fn run(&self, arguments: Vec<String>) -> CommandResult {
if arguments.is_empty() {
CommandResult::Continue(None)
} else {
CommandResult::Continue(Some(arguments[0].clone()))
}
}
}
#[derive(Clone)]
pub(crate) struct SetHandleCommand {}
impl Command for SetHandleCommand {
fn name(&self) -> String {
"test_set_handle".to_string()
}
fn clone_and_box(&self) -> Box<dyn Command> {
Box::new((*self).clone())
}
fn requires_context(&self) -> bool {
true
}
fn run_with_context(
&self,
arguments: Vec<String>,
state: &mut HashMap<String, StateValue>,
_variables: &mut HashMap<String, String>,
_output_variable: Option<String>,
_instructions: &Vec<Instruction>,
_commands: &mut Commands,
_line: usize,
) -> CommandResult {
if arguments.is_empty() {
CommandResult::Continue(None)
} else {
let state = get_handles_sub_state(state);
state.insert(arguments[0].clone(), StateValue::String("test".to_string()));
CommandResult::Continue(None)
}
}
}
#[derive(Clone)]
pub(crate) struct ArrayCommand {}
impl Command for ArrayCommand {
fn name(&self) -> String {
"test_array".to_string()
}
fn clone_and_box(&self) -> Box<dyn Command> {
Box::new((*self).clone())
}
fn requires_context(&self) -> bool {
true
}
fn run_with_context(
&self,
arguments: Vec<String>,
state: &mut HashMap<String, StateValue>,
_variables: &mut HashMap<String, String>,
_output_variable: Option<String>,
_instructions: &Vec<Instruction>,
_commands: &mut Commands,
_line: usize,
) -> CommandResult {
let mut array = vec![];
for argument in arguments {
array.push(StateValue::String(argument));
}
let key = put_handle(state, StateValue::List(array));
CommandResult::Continue(Some(key))
}
}
#[derive(Clone)]
pub(crate) struct OnErrorCommand {}
impl Command for OnErrorCommand {
fn name(&self) -> String {
"on_error".to_string()
}
fn clone_and_box(&self) -> Box<dyn Command> {
Box::new((*self).clone())
}
fn requires_context(&self) -> bool {
true
}
fn run_with_context(
&self,
arguments: Vec<String>,
_state: &mut HashMap<String, StateValue>,
variables: &mut HashMap<String, String>,
_output_variable: Option<String>,
_instructions: &Vec<Instruction>,
_commands: &mut Commands,
_line: usize,
) -> CommandResult {
println!("on error: {:#?}", &arguments);
let mut index = 0;
for argument in arguments {
index = index + 1;
variables.insert(index.to_string(), argument.clone());
}
variables.insert("on_error_invoked".to_string(), "true".to_string());
CommandResult::Continue(None)
}
}
pub(crate) enum CommandValidation {
None,
PositiveNumber(String),
Match(String, String),
Contains(String, String),
Any(String, Vec<String>),
}
pub(crate) fn test_common_command_functions(command: Box<dyn Command>) {
assert!(command.name().len() > 0);
command.help();
command.aliases();
}
fn run_command(commands: Vec<Box<dyn Command>>, script: &str) -> Result<Context, ScriptError> {
let mut context = Context::new();
for command in commands {
context.commands.set(command)?;
}
if !context.commands.exists("on_error") {
let added = context.commands.set(Box::new(OnErrorCommand {}));
assert!(added.is_ok());
}
runner::run_script(script, context)
}
pub(crate) fn run_script_and_crash(commands: Vec<Box<dyn Command>>, script: &str) {
let result = run_command(commands, script);
assert!(result.is_err());
}
pub(crate) fn run_script_and_error(
commands: Vec<Box<dyn Command>>,
script: &str,
output_variable: &str,
) -> Context {
let result = run_command(commands, script);
match result {
Ok(context) => {
assert_eq!(
context.variables.get(&output_variable.to_string()).unwrap(),
"false"
);
assert_eq!(
context
.variables
.get(&"on_error_invoked".to_string())
.unwrap(),
"true"
);
context
}
Err(error) => panic!(error.to_string()),
}
}
pub(crate) fn run_script_and_validate(
commands: Vec<Box<dyn Command>>,
script: &str,
validation: CommandValidation,
) -> Context {
let result = run_command(commands, script);
match result {
Ok(context) => {
assert!(context
.variables
.get(&"on_error_invoked".to_string())
.is_none());
match validation {
CommandValidation::None => assert!(context.variables.is_empty()),
CommandValidation::Match(key, value) => {
assert!(!context.variables.is_empty());
assert_eq!(context.variables.get(&key), Some(&value))
}
CommandValidation::Contains(key, value) => {
assert!(!context.variables.is_empty());
let var_value = context.variables.get(&key).unwrap();
assert!(
var_value.contains(&value),
"The value: {} is not contained in: {}",
&value,
&var_value
)
}
CommandValidation::Any(key, values) => {
assert!(!context.variables.is_empty());
let var_value = context.variables.get(&key).unwrap();
assert!(
values.contains(&var_value),
"The value: {} is not contained in: {:#?}",
&var_value,
&values
)
}
CommandValidation::PositiveNumber(key) => {
assert!(!context.variables.is_empty());
let var_value = context.variables.get(&key).unwrap();
let numeric_value: u128 = var_value.parse().unwrap();
assert!(numeric_value > 0)
}
};
context
}
Err(error) => panic!(error.to_string()),
}
}
pub(crate) fn is_handles_empty(state: &HashMap<String, StateValue>) {
let handles_state = state.get("handles").unwrap();
match handles_state {
StateValue::SubState(state) => assert!(state.is_empty()),
_ => panic!("Invalid state type."),
}
}