use crate::arg_parser::ArgParser;
use crate::models::MacroType;
use crate::models::RadResult;
use crate::models::{ExtMacroBody, ExtMacroBuilder};
use crate::utils::Utils;
use crate::Processor;
use crate::{AuthType, RadError};
use std::collections::HashMap;
use std::iter::FromIterator;
use crate::formatter::Formatter;
pub(crate) type DFunctionMacroType = fn(&str, usize, &mut Processor) -> RadResult<Option<String>>;
#[derive(Clone)]
pub struct DeterredMacroMap {
pub(crate) macros: HashMap<String, KMacroSign>,
}
impl DeterredMacroMap {
pub fn empty() -> Self {
Self {
macros: HashMap::new(),
}
}
pub fn new() -> Self {
let mut map = HashMap::from_iter(IntoIterator::into_iter([
(
"exec".to_owned(),
KMacroSign::new(
"exec",
["macro_name", "macro_args"],
DeterredMacroMap::execute_macro,
None,
),
),
(
"fassert".to_owned(),
KMacroSign::new(
"fassert",
["a_lvalue", "a_rvalue"],
DeterredMacroMap::assert_fail,
None,
),
),
(
"foreach".to_owned(),
KMacroSign::new(
"foreach",
["a_array", "a_body"],
DeterredMacroMap::foreach,
None,
),
),
(
"forline".to_owned(),
KMacroSign::new(
"forline",
["a_iterable", "a_body"],
DeterredMacroMap::forline,
None,
),
),
(
"forloop".to_owned(),
KMacroSign::new(
"forloop",
["a_min", "a_max", "a_body"],
DeterredMacroMap::forloop,
None,
),
),
(
"from".to_owned(),
KMacroSign::new(
"from",
["a_macro_name", "a_csv_value"],
Self::from_data,
None,
),
),
(
"if".to_owned(),
KMacroSign::new(
"if",
["a_boolean", "a_if_expr"],
DeterredMacroMap::if_cond,
None,
),
),
(
"ifelse".to_owned(),
KMacroSign::new(
"ifelse",
["a_boolean", "a_if_expr", "a_else_expr"],
DeterredMacroMap::ifelse,
None,
),
),
(
"ifdef".to_owned(),
KMacroSign::new(
"ifdef",
["a_macro_name", "a_if_expr"],
DeterredMacroMap::ifdef,
None,
),
),
(
"ifdefel".to_owned(),
KMacroSign::new(
"ifdefel",
["a_macro_name", "a_if_expr", "a_else_expr"],
DeterredMacroMap::ifdefel,
None,
),
),
(
"que".to_owned(),
KMacroSign::new("que", ["a_content"], DeterredMacroMap::queue_content, None),
),
(
"ifque".to_owned(),
KMacroSign::new(
"ifque",
["a_bool", "a_content"],
DeterredMacroMap::if_queue_content,
None,
),
),
]));
#[cfg(not(feature = "wasm"))]
{
map.insert(
"ifenv".to_owned(),
KMacroSign::new(
"ifenv",
["a_env_name", "a_if_expr"],
DeterredMacroMap::ifenv,
None,
),
);
map.insert(
"ifenvel".to_owned(),
KMacroSign::new(
"ifenvel",
["a_env_name", "a_if_expr", "a_else_expr"],
DeterredMacroMap::ifenvel,
None,
),
);
}
#[cfg(feature = "evalexpr")]
{
map.insert(
"ieval".to_owned(),
KMacroSign::new("ieval", ["a_macro","a_expression"], Self::eval_inplace, None),
);
}
Self { macros: map }
}
pub fn get_deterred_macro(&self, name: &str) -> Option<&DFunctionMacroType> {
if let Some(mac) = self.macros.get(name) {
Some(&mac.logic)
} else {
None
}
}
pub fn contains(&self, name: &str) -> bool {
self.macros.contains_key(name)
}
pub fn undefine(&mut self, name: &str) {
self.macros.remove(name);
}
pub fn rename(&mut self, name: &str, target: &str) {
let func = self.macros.remove(name).unwrap();
self.macros.insert(target.to_owned(), func);
}
pub fn new_ext_macro(&mut self, ext: ExtMacroBuilder) {
if let Some(ExtMacroBody::Deterred(mac_ref)) = ext.macro_body {
let sign = KMacroSign::new(&ext.macro_name, &ext.args, mac_ref, ext.macro_desc);
self.macros.insert(ext.macro_name, sign);
}
}
fn foreach(args: &str, level: usize, processor: &mut Processor) -> RadResult<Option<String>> {
if let Some(args) = ArgParser::new().args_with_len(args, 2) {
let mut sums = String::new();
let loopable = &processor.parse_chunk_args(level, "", &args[0])?;
let mut count = 0;
for value in loopable.split(',') {
processor.add_new_local_macro(level, "a_LN", &count.to_string());
let result : String;
#[cfg(feature = "for_macro")]
{
processor.add_new_local_macro(level, ":", value);
result = processor.parse_chunk_args(level, "", &args[1])?;
}
#[cfg(not(feature = "for_macro"))]
{ result = processor.parse_chunk_args(level, "", &args[1].replace("$:", &value.to_string()))?; }
sums.push_str(&result);
count += 1;
}
#[cfg(feature = "for_macro")]
processor.remove_local_macro(level, ":");
Ok(Some(sums))
} else {
Err(RadError::InvalidArgument(
"Foreach requires two argument".to_owned(),
))
}
}
fn forline(args: &str, level: usize, processor: &mut Processor) -> RadResult<Option<String>> {
if let Some(args) = ArgParser::new().args_with_len(args, 2) {
let mut sums = String::new();
let loopable = &processor.parse_chunk_args(level, "", &args[0])?;
let mut count = 1;
for value in loopable.lines() {
processor.add_new_local_macro(level, "a_LN", &count.to_string());
let result =
processor.parse_chunk_args(level, "", &args[1].replace("$:", value))?;
sums.push_str(&result);
count += 1;
}
Ok(Some(sums))
} else {
Err(RadError::InvalidArgument(
"Forline requires two argument".to_owned(),
))
}
}
fn forloop(args: &str, level: usize, processor: &mut Processor) -> RadResult<Option<String>> {
if let Some(args) = ArgParser::new().args_with_len(args, 3) {
let mut sums = String::new();
let min_src = processor.parse_chunk_args(level, "", &Utils::trim(&args[0]))?;
let max_src = processor.parse_chunk_args(level, "", &Utils::trim(&args[1]))?;
let min: usize;
let max: usize;
if let Ok(num) = min_src.parse::<usize>() {
min = num;
} else {
return Err(RadError::InvalidArgument(format!(
"Forloop's min value should be non zero positive integer but given {}",
min_src
)));
}
if let Ok(num) = max_src.parse::<usize>() {
max = num
} else {
return Err(RadError::InvalidArgument(format!(
"Forloop's max value should be non zero positive integer but given \"{}\"",
max_src
)));
}
let mut result: String;
for value in min..=max {
#[cfg(feature = "for_macro")]
{
processor.add_new_local_macro(level, ":", &value.to_string());
result = processor.parse_chunk_args(
level,
"",
&args[2],
)?;
}
#[cfg(not(feature = "for_macro"))]
{
result = processor.parse_chunk_args(
level,
"",
&args[2].replace("$:", &value.to_string())
)?;
}
sums.push_str(&result);
result.clear();
}
#[cfg(feature = "for_macro")]
processor.remove_local_macro(level, ":");
Ok(Some(sums))
} else {
Err(RadError::InvalidArgument(
"Forloop requires two argument".to_owned(),
))
}
}
fn if_cond(args: &str, level: usize, processor: &mut Processor) -> RadResult<Option<String>> {
if let Some(args) = ArgParser::new().args_with_len(args, 2) {
let boolean = &processor.parse_chunk_args(level, "", &args[0])?;
let cond = Utils::is_arg_true(&Utils::trim(boolean));
if let Ok(cond) = cond {
if cond {
let if_expr = processor.parse_chunk_args(level, "", &args[1])?;
return Ok(Some(if_expr));
}
} else {
return Err(RadError::InvalidArgument(format!(
"If requires either true/false or zero/nonzero integer but given \"{}\"",
boolean
)));
}
Ok(None)
} else {
Err(RadError::InvalidArgument(
"if requires two arguments".to_owned(),
))
}
}
fn ifelse(args: &str, level: usize, processor: &mut Processor) -> RadResult<Option<String>> {
if let Some(args) = ArgParser::new().args_with_len(args, 3) {
let boolean = &processor.parse_chunk_args(level, "", &args[0])?;
let cond = Utils::is_arg_true(&Utils::trim(boolean));
if let Ok(cond) = cond {
if cond {
let if_expr = processor.parse_chunk_args(level, "", &args[1])?;
return Ok(Some(if_expr));
}
} else {
return Err(RadError::InvalidArgument(format!(
"Ifelse requires either true/false or zero/nonzero integer but given \"{}\"",
boolean
)));
}
let else_expr = processor.parse_chunk_args(level, "", &args[2])?;
return Ok(Some(else_expr));
} else {
Err(RadError::InvalidArgument(
"ifelse requires three argument".to_owned(),
))
}
}
fn ifdef(args: &str, level: usize, processor: &mut Processor) -> RadResult<Option<String>> {
if let Some(args) = ArgParser::new().args_with_len(args, 2) {
let name = processor.parse_chunk_args(level, "", &Utils::trim(&args[0]))?;
let boolean = processor.contains_macro(&name, MacroType::Any);
if boolean {
let if_expr = processor.parse_chunk_args(level, "", &args[1])?;
return Ok(Some(if_expr));
}
Ok(None)
} else {
Err(RadError::InvalidArgument(
"ifdef requires two arguments".to_owned(),
))
}
}
fn ifdefel(args: &str, level: usize, processor: &mut Processor) -> RadResult<Option<String>> {
if let Some(args) = ArgParser::new().args_with_len(args, 3) {
let name = processor.parse_chunk_args(level, "", &Utils::trim(&args[0]))?;
let boolean = processor.contains_macro(&name, MacroType::Any);
if boolean {
let if_expr = processor.parse_chunk_args(level, "", &args[1])?;
return Ok(Some(if_expr));
} else {
let else_expr = processor.parse_chunk_args(level, "", &args[2])?;
return Ok(Some(else_expr));
}
} else {
Err(RadError::InvalidArgument(
"ifdefel requires three arguments".to_owned(),
))
}
}
fn ifenv(args: &str, level: usize, processor: &mut Processor) -> RadResult<Option<String>> {
if !Utils::is_granted("ifenv", AuthType::ENV, processor)? {
return Ok(None);
}
if let Some(args) = ArgParser::new().args_with_len(args, 2) {
let name = processor.parse_chunk_args(level, "", &Utils::trim(&args[0]))?;
let boolean = if let Ok(_) = std::env::var(name) {
true
} else {
false
};
if boolean {
let if_expr = processor.parse_chunk_args(level, "", &args[1])?;
return Ok(Some(if_expr));
}
Ok(None)
} else {
Err(RadError::InvalidArgument(
"ifenv requires two arguments".to_owned(),
))
}
}
fn ifenvel(args: &str, level: usize, processor: &mut Processor) -> RadResult<Option<String>> {
if !Utils::is_granted("ifenvel", AuthType::ENV, processor)? {
return Ok(None);
}
if let Some(args) = ArgParser::new().args_with_len(args, 3) {
let name = processor.parse_chunk_args(level, "", &Utils::trim(&args[0]))?;
let boolean = if let Ok(_) = std::env::var(name) {
true
} else {
false
};
if boolean {
let if_expr = processor.parse_chunk_args(level, "", &args[1])?;
return Ok(Some(if_expr));
} else {
let else_expr = processor.parse_chunk_args(level, "", &args[2])?;
return Ok(Some(else_expr));
}
} else {
Err(RadError::InvalidArgument(
"ifenvel requires three arguments".to_owned(),
))
}
}
fn assert_fail(
args: &str,
level: usize,
processor: &mut Processor,
) -> RadResult<Option<String>> {
let result = processor.parse_chunk_args(level, "", args);
if let Err(_) = result {
processor.track_assertion(true)?;
Ok(None)
} else {
processor.track_assertion(false)?;
Err(RadError::AssertFail)
}
}
fn queue_content(args: &str, _: usize, processor: &mut Processor) -> RadResult<Option<String>> {
processor.insert_queue(args);
Ok(None)
}
fn if_queue_content(
args: &str,
level: usize,
processor: &mut Processor,
) -> RadResult<Option<String>> {
if let Some(args) = ArgParser::new().args_with_len(args, 2) {
let boolean = &processor.parse_chunk_args(level, "", &args[0])?;
let cond = Utils::is_arg_true(&boolean)?;
if cond {
processor.insert_queue(&args[1]);
}
Ok(None)
} else {
Err(RadError::InvalidArgument(
"ifque requires two argument".to_owned(),
))
}
}
fn execute_macro(
args: &str,
level: usize,
processor: &mut Processor,
) -> RadResult<Option<String>> {
if let Some(args) = ArgParser::new().args_with_len(args, 2) {
let macro_name = &processor.parse_chunk_args(level, "", &args[0])?;
if !processor.contains_macro(¯o_name, MacroType::Any) {
return Err(RadError::InvalidArgument(
format!("Macro \"{}\" doesn't exist", macro_name)
));
}
let args = &args[1];
let result = processor.parse_chunk_args(level, "", &format!("${}({})",macro_name, args))?;
Ok(Some(result))
} else {
Err(RadError::InvalidArgument(
"exec requires two argument".to_owned(),
))
}
}
fn from_data(args: &str,level: usize, processor: &mut Processor) -> RadResult<Option<String>> {
if let Some(args) = ArgParser::new().args_with_len(args, 2) {
let macro_name = Utils::trim(&args[0]);
let macro_data = &args[1];
let result = Formatter::csv_to_macros(¯o_name, macro_data, &processor.state.newline)?;
#[cfg(feature = "debug")]
let original = processor.is_debug();
#[cfg(feature = "debug")]
processor.set_debug(false);
let result = processor.parse_chunk_args(level, "", &result)?;
#[cfg(feature = "debug")]
{
use crate::debugger::DebugSwitch;
processor.set_debug(original);
match processor.get_debug_switch() {
DebugSwitch::StepMacro | DebugSwitch::NextMacro => {
processor.set_prompt_log("\"From macro\"")
}
_ => (),
}
}
Ok(Some(result))
} else {
Err(RadError::InvalidArgument(
"From requires two arguments".to_owned(),
))
}
}
#[cfg(feature = "evalexpr")]
fn eval_inplace(args: &str,level: usize, processor: &mut Processor) -> RadResult<Option<String>> {
if let Some(args) = ArgParser::new().args_with_len(args, 2) {
let macro_name = Utils::trim(&args[0]);
if !processor.contains_macro(¯o_name, MacroType::Runtime) {
return Err(RadError::InvalidArgument(format!("Macro \"{}\" doesn't exist", macro_name)));
}
let expr = Utils::trim(&args[1]);
let chunk = format!("$eval( ${}() {} )",macro_name, expr);
let result = processor.parse_chunk_args(level, "", &chunk)?;
processor.replace_macro(¯o_name, &result);
Ok(None)
} else {
Err(RadError::InvalidArgument(
"Ieval requires two arguments".to_owned(),
))
}
}
}
#[derive(Clone)]
pub(crate) struct KMacroSign {
name: String,
args: Vec<String>,
pub logic: DFunctionMacroType,
#[allow(dead_code)]
desc: Option<String>,
}
impl KMacroSign {
pub fn new(
name: &str,
args: impl IntoIterator<Item = impl AsRef<str>>,
logic: DFunctionMacroType,
desc: Option<String>,
) -> Self {
let args = args
.into_iter()
.map(|s| s.as_ref().to_owned())
.collect::<Vec<String>>();
Self {
name: name.to_owned(),
args,
logic,
desc,
}
}
}
impl std::fmt::Display for KMacroSign {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let mut inner = self
.args
.iter()
.fold(String::new(), |acc, arg| acc + &arg + ",");
inner.pop();
write!(f, "${}({})", self.name, inner)
}
}
#[cfg(feature = "signature")]
impl From<&KMacroSign> for crate::sigmap::MacroSignature {
fn from(ms: &KMacroSign) -> Self {
Self {
variant: crate::sigmap::MacroVariant::Deterred,
name: ms.name.to_owned(),
args: ms.args.to_owned(),
expr: ms.to_string(),
desc: ms.desc.clone(),
}
}
}