#[cfg(not(feature = "wasm"))]
use crate::auth::AuthType;
#[cfg(not(feature = "wasm"))]
use crate::common::{ContainerType, FileTarget, FlowControl, ProcessInput};
use crate::common::{ErrorBehaviour, MacroType, RadResult, RelayTarget, STREAM_CONTAINER};
use crate::consts::MACRO_SPECIAL_ANON;
use crate::deterred_map::DeterredMacroMap;
use crate::formatter::Formatter;
use crate::parser::SplitVariant;
use crate::utils::Utils;
use crate::ArgParser;
use crate::WarningType;
use crate::{trim, Processor, RadError};
use std::fs::File;
use std::io::{BufRead, BufReader};
#[cfg(not(feature = "wasm"))]
use std::path::PathBuf;
impl DeterredMacroMap {
pub(crate) fn add_anonymous_macro(
args: &str,
_: usize,
p: &mut Processor,
) -> RadResult<Option<String>> {
p.add_anon_macro(args)?;
Ok(Some(String::from(MACRO_SPECIAL_ANON)))
}
pub(crate) fn append(
args: &str,
level: usize,
processor: &mut Processor,
) -> RadResult<Option<String>> {
let mut ap = ArgParser::new().no_strip();
let args = ap.args_to_vec(args, ',', SplitVariant::Never);
ap.set_strip(true);
if args.len() >= 2 {
let name =
processor.parse_and_strip(&mut ap, level, "append", trim!(&args[0]).as_ref())?;
let target = processor.parse_and_strip(&mut ap, level, "append", &args[1])?;
let mut trailer = None;
if args.len() >= 3 {
trailer = Some(processor.parse_and_strip(&mut ap, level, "append", &args[2])?);
}
if let Some(name) = processor.contains_local_macro(level, &name) {
if let Some(tt) = trailer {
let body = processor.get_local_macro_body(&name)?;
if !body.ends_with(&tt) && !body.is_empty() {
processor.append_local_macro(&name, &format!("{}{}", tt, target));
return Ok(None);
}
}
processor.append_local_macro(&name, &target);
} else if processor.contains_macro(&name, MacroType::Runtime) {
if let Some(tt) = trailer {
let body = processor.get_runtime_macro_body(&name)?;
if !body.ends_with(&tt) && !body.is_empty() {
processor.append_macro(&name, &format!("{}{}", tt, target));
return Ok(None);
}
}
processor.append_macro(&name, &target);
} else {
return Err(RadError::InvalidArgument(format!(
"Macro \"{}\" doesn't exist",
name
)));
}
Ok(None)
} else {
Err(RadError::InvalidArgument(
"Append at least requires two arguments".to_owned(),
))
}
}
pub(crate) fn map_array(
args: &str,
level: usize,
p: &mut Processor,
) -> RadResult<Option<String>> {
let mut ap = ArgParser::new().no_strip();
if let Some(args) = ap.args_with_len(args, 2) {
ap.set_strip(true);
let macro_name = p.parse_and_strip(&mut ap, level, "mapl", &trim!(&args[0]))?;
let src = p.parse_and_strip(&mut ap, level, "map", &args[1])?;
let array = src.split(',');
let mut acc = String::new();
for item in array {
acc.push_str(
&p.execute_macro(level, "map", ¯o_name, item)?
.unwrap_or_default(),
);
}
Ok(Some(acc))
} else {
Err(RadError::InvalidArgument(
"map requires two arguments".to_owned(),
))
}
}
pub(crate) fn map_lines(
args: &str,
level: usize,
p: &mut Processor,
) -> RadResult<Option<String>> {
let mut ap = ArgParser::new().no_strip();
if let Some(args) = ap.args_with_len(args, 2) {
ap.set_strip(true);
let macro_name = p.parse_and_strip(&mut ap, level, "mapl", &trim!(&args[0]))?;
let src = p.parse_and_strip(&mut ap, level, "mapl", &args[1])?;
let lines = src.lines();
let mut acc = String::new();
for item in lines {
acc.push_str(
&p.execute_macro(level, "mapl", ¯o_name, item)?
.unwrap_or_default(),
);
}
Ok(Some(acc))
} else {
Err(RadError::InvalidArgument(
"mapl requires two arguments".to_owned(),
))
}
}
#[cfg(not(feature = "wasm"))]
pub(crate) fn map_file(
args: &str,
level: usize,
p: &mut Processor,
) -> RadResult<Option<String>> {
if !Utils::is_granted("mapf", AuthType::FIN, p)? {
return Ok(None);
}
let mut ap = ArgParser::new().no_strip();
if let Some(args) = ap.args_with_len(args, 2) {
ap.set_strip(true);
let macro_name = p.parse_and_strip(&mut ap, level, "mapl", &trim!(&args[0]))?;
let file = BufReader::new(std::fs::File::open(p.parse_and_strip(
&mut ap,
level,
"mapf",
&trim!(&args[1]),
)?)?)
.lines();
let mut acc = String::new();
for line in file {
let line = line?;
acc.push_str(
&p.execute_macro(level, "mapf", ¯o_name, &line)?
.unwrap_or_default(),
);
}
Ok(Some(acc))
} else {
Err(RadError::InvalidArgument(
"mapf requires two arguments".to_owned(),
))
}
}
pub(crate) fn grep_map(
args: &str,
level: usize,
p: &mut Processor,
) -> RadResult<Option<String>> {
let mut ap = ArgParser::new().no_strip();
if let Some(args) = ap.args_with_len(args, 4) {
ap.set_strip(true);
let grep_type = p.parse_and_strip(&mut ap, level, "grepmap", &trim!(&args[0]))?;
let match_expr = p.parse_and_strip(&mut ap, level, "grepmap", &args[1])?;
let macro_name = p.parse_and_strip(&mut ap, level, "grepmap", &trim!(&args[2]))?;
let source = p.parse_and_strip(&mut ap, level, "grepmap", &args[3])?;
let bufread = match grep_type.to_lowercase().as_str() {
#[cfg(not(feature = "wasm"))]
"file" => {
if !Utils::is_granted("grepmap", AuthType::FIN, p)? {
return Ok(None);
}
true
}
"text" => false,
_ => {
return Err(RadError::InvalidArgument(format!(
"{} is not a valid grep type",
grep_type
)))
}
};
if bufread && !std::path::Path::new(&source).exists() {
return Err(RadError::InvalidArgument(format!(
"Cannot find a file \"{}\" ",
source
)));
}
if match_expr.is_empty() {
return Err(RadError::InvalidArgument(
"Regex expression cannot be an empty string".to_string(),
));
}
let mut res = String::new();
let reg = p.try_get_or_insert_regex(&match_expr)?.clone();
if !bufread {
for cap in reg.captures_iter(&source) {
let captured = cap.get(0).map_or("", |m| m.as_str());
let expanded = p
.execute_macro(level, "grepmap", ¯o_name, captured)?
.unwrap_or_default();
res.push_str(&expanded);
}
} else {
let lines = BufReader::new(File::open(std::path::Path::new(&source))?).lines();
for line in lines {
let line = line?;
for cap in reg.captures_iter(&line) {
let captured = cap.get(0).map_or("", |m| m.as_str());
let expanded = p
.execute_macro(level, "grepmap", ¯o_name, captured)?
.unwrap_or_default();
res.push_str(&expanded);
}
}
}
Ok(Some(res))
} else {
Err(RadError::InvalidArgument(
"grepamp requires four arguments".to_owned(),
))
}
}
pub(crate) fn forby(
args: &str,
level: usize,
processor: &mut Processor,
) -> RadResult<Option<String>> {
let mut ap = ArgParser::new().no_strip();
if let Some(args) = ap.args_with_len(args, 3) {
ap.set_strip(true);
let mut sums = String::new();
let body = &args[0];
let sep = &processor.parse_and_strip(&mut ap, level, "forby", &args[1])?;
let loopable = &processor.parse_and_strip(&mut ap, level, "forby", &args[2])?;
for (count, value) in loopable.split_terminator(sep).enumerate() {
processor.add_new_local_macro(level, "a_LN", &count.to_string());
processor.add_new_local_macro(level, ":", value);
let result = &processor.parse_and_strip(&mut ap, level, "forby", body)?;
sums.push_str(result);
}
processor.remove_local_macro(level, ":");
Ok(Some(sums))
} else {
Err(RadError::InvalidArgument(
"Foreach requires two argument".to_owned(),
))
}
}
pub(crate) fn foreach(
args: &str,
level: usize,
processor: &mut Processor,
) -> RadResult<Option<String>> {
let mut ap = ArgParser::new().no_strip();
if let Some(args) = ap.args_with_len(args, 2) {
ap.set_strip(true);
let mut sums = String::new();
let body = &args[0];
let loop_src = processor.parse_and_strip(&mut ap, level, "foreach", &args[1])?;
let loopable = trim!(&loop_src);
for (count, value) in loopable.as_ref().split(',').enumerate() {
processor.add_new_local_macro(level, "a_LN", &count.to_string());
processor.add_new_local_macro(level, ":", value);
let result = &processor.parse_and_strip(&mut ap, level, "foreach", body)?;
sums.push_str(result);
}
processor.remove_local_macro(level, ":");
Ok(Some(sums))
} else {
Err(RadError::InvalidArgument(
"Foreach requires two argument".to_owned(),
))
}
}
pub(crate) fn forline(
args: &str,
level: usize,
processor: &mut Processor,
) -> RadResult<Option<String>> {
let mut ap = ArgParser::new().no_strip();
if let Some(args) = ap.args_with_len(args, 2) {
ap.set_strip(true);
let mut sums = String::new();
let body = &args[0];
let loop_src = processor.parse_and_strip(&mut ap, level, "forline", &args[1])?;
let loopable = trim!(&loop_src);
let mut count = 1;
for value in loopable.lines() {
processor.add_new_local_macro(level, "a_LN", &count.to_string());
processor.add_new_local_macro(level, ":", value);
let result = processor.parse_and_strip(&mut ap, level, "forline", body)?;
sums.push_str(&result);
count += 1;
}
Ok(Some(sums))
} else {
Err(RadError::InvalidArgument(
"Forline requires two argument".to_owned(),
))
}
}
pub(crate) fn forloop(
args: &str,
level: usize,
processor: &mut Processor,
) -> RadResult<Option<String>> {
let mut ap = ArgParser::new().no_strip();
if let Some(args) = ap.args_with_len(args, 3) {
ap.set_strip(true);
let mut sums = String::new();
let body = &args[0];
let min_src =
trim!(&processor.parse_and_strip(&mut ap, level, "forloop", &args[1])?).to_string();
let max_src =
trim!(&processor.parse_and_strip(&mut ap, level, "forloop", &args[2])?).to_string();
let min = if let Ok(num) = min_src.parse::<usize>() {
num
} else {
return Err(RadError::InvalidArgument(format!(
"Forloop's min value should be non zero positive integer but given {}",
min_src
)));
};
let max = if let Ok(num) = max_src.parse::<usize>() {
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 {
processor.add_new_local_macro(level, ":", &value.to_string());
result = processor.parse_and_strip(&mut ap, level, "forloop", body)?;
sums.push_str(&result);
result.clear();
}
processor.remove_local_macro(level, ":");
Ok(Some(sums))
} else {
Err(RadError::InvalidArgument(
"Forloop requires two argument".to_owned(),
))
}
}
pub(crate) fn log_macro_info(
args: &str,
level: usize,
p: &mut Processor,
) -> RadResult<Option<String>> {
let mut ap = ArgParser::new();
let macro_name = trim!(&p.parse_and_strip(&mut ap, level, "logm", args)?).to_string();
let body = if let Some(name) = p.contains_local_macro(level, ¯o_name) {
p.get_local_macro_body(&name)?.to_string()
} else if let Ok(body) = p.get_runtime_macro_body(¯o_name) {
body.to_string()
} else {
return Err(RadError::InvalidArgument(format!(
"Macro \"{}\" doesn't exist",
¯o_name
)));
};
p.log_message(&body)?;
Ok(None)
}
pub(crate) fn if_cond(
args: &str,
level: usize,
processor: &mut Processor,
) -> RadResult<Option<String>> {
let mut ap = ArgParser::new().no_strip();
if let Some(args) = ap.args_with_len(args, 2) {
ap.set_strip(true);
let boolean = &processor.parse_and_strip(&mut ap, level, "if", &args[0])?;
let cond = Utils::is_arg_true(boolean);
if let Ok(cond) = cond {
if cond {
let if_expr = processor.parse_and_strip(&mut ap, level, "if", &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(),
))
}
}
pub(crate) fn ifelse(
args: &str,
level: usize,
processor: &mut Processor,
) -> RadResult<Option<String>> {
let mut ap = ArgParser::new().no_strip();
if let Some(args) = ap.args_with_len(args, 3) {
ap.set_strip(true);
let boolean = &processor.parse_and_strip(&mut ap, level, "ifelse", &args[0])?;
let cond = Utils::is_arg_true(boolean);
if let Ok(cond) = cond {
if cond {
let if_expr = processor.parse_and_strip(&mut ap, level, "ifelse", &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_and_strip(&mut ap, level, "ifelse", &args[2])?;
Ok(Some(else_expr))
} else {
Err(RadError::InvalidArgument(
"ifelse requires three argument".to_owned(),
))
}
}
pub(crate) fn ifdef(
args: &str,
level: usize,
processor: &mut Processor,
) -> RadResult<Option<String>> {
let mut ap = ArgParser::new().no_strip();
if let Some(args) = ap.args_with_len(args, 2) {
ap.set_strip(true);
let name =
trim!(&processor.parse_and_strip(&mut ap, level, "ifdef", &args[0])?).to_string();
let boolean = processor.contains_macro(&name, MacroType::Any);
if boolean {
let if_expr = processor.parse_and_strip(&mut ap, level, "ifdef", &args[1])?;
return Ok(Some(if_expr));
}
Ok(None)
} else {
Err(RadError::InvalidArgument(
"ifdef requires two arguments".to_owned(),
))
}
}
pub(crate) fn ifdefel(
args: &str,
level: usize,
processor: &mut Processor,
) -> RadResult<Option<String>> {
let mut ap = ArgParser::new().no_strip();
if let Some(args) = ap.args_with_len(args, 3) {
ap.set_strip(true);
let name =
trim!(&processor.parse_and_strip(&mut ap, level, "ifdefel", &args[0])?).to_string();
let boolean = processor.contains_macro(&name, MacroType::Any);
if boolean {
let if_expr = processor.parse_and_strip(&mut ap, level, "ifdefel", &args[1])?;
Ok(Some(if_expr))
} else {
let else_expr = processor.parse_and_strip(&mut ap, level, "ifdefel", &args[2])?;
Ok(Some(else_expr))
}
} else {
Err(RadError::InvalidArgument(
"ifdefel requires three arguments".to_owned(),
))
}
}
#[cfg(not(feature = "wasm"))]
pub(crate) fn ifenv(
args: &str,
level: usize,
processor: &mut Processor,
) -> RadResult<Option<String>> {
if !Utils::is_granted("ifenv", AuthType::ENV, processor)? {
return Ok(None);
}
let mut ap = ArgParser::new().no_strip();
if let Some(args) = ap.args_with_len(args, 2) {
ap.set_strip(true);
let name =
trim!(&processor.parse_and_strip(&mut ap, level, "ifenv", &args[0])?).to_string();
let boolean = std::env::var(name).is_ok();
if boolean {
let if_expr = processor.parse_and_strip(&mut ap, level, "ifenv", &args[1])?;
return Ok(Some(if_expr));
}
Ok(None)
} else {
Err(RadError::InvalidArgument(
"ifenv requires two arguments".to_owned(),
))
}
}
#[cfg(not(feature = "wasm"))]
pub(crate) fn ifenvel(
args: &str,
level: usize,
processor: &mut Processor,
) -> RadResult<Option<String>> {
if !Utils::is_granted("ifenvel", AuthType::ENV, processor)? {
return Ok(None);
}
let mut ap = ArgParser::new().no_strip();
if let Some(args) = ap.args_with_len(args, 3) {
ap.set_strip(true);
let name =
trim!(&processor.parse_and_strip(&mut ap, level, "ifenvel", &args[0])?).to_string();
let boolean = std::env::var(name).is_ok();
if boolean {
let if_expr = processor.parse_and_strip(&mut ap, level, "ifenvel", &args[1])?;
Ok(Some(if_expr))
} else {
let else_expr = processor.parse_and_strip(&mut ap, level, "ifenvel", &args[2])?;
Ok(Some(else_expr))
}
} else {
Err(RadError::InvalidArgument(
"ifenvel requires three arguments".to_owned(),
))
}
}
pub(crate) fn expand_expression(
args: &str,
level: usize,
processor: &mut Processor,
) -> RadResult<Option<String>> {
let args = ArgParser::new().strip(args);
let result = processor.parse_chunk_args(level, "", &args)?;
Ok(if result.is_empty() {
None
} else {
Some(result)
})
}
pub(crate) fn assert_fail(
args: &str,
level: usize,
processor: &mut Processor,
) -> RadResult<Option<String>> {
let backup = processor.state.behaviour;
processor.state.behaviour = ErrorBehaviour::Assert;
let mut ap = ArgParser::new().no_strip();
let result = processor.parse_and_strip(&mut ap, level, "fassert", args);
processor.state.behaviour = backup;
if result.is_err() {
processor.track_assertion(true)?;
Ok(None)
} else {
processor.track_assertion(false)?;
Err(RadError::AssertFail)
}
}
pub(crate) fn consume(_: &str, level: usize, p: &mut Processor) -> RadResult<Option<String>> {
p.state.relay.pop();
let mut ap = ArgParser::new().no_strip();
ap.set_strip(true);
let macro_name_src = std::mem::take(&mut p.state.stream_state.macro_name_src);
let macro_name = p.parse_and_strip(&mut ap, level, "consume", ¯o_name_src)?;
let content = p.get_runtime_macro_body(STREAM_CONTAINER)?.to_owned();
let result = if p.state.stream_state.as_lines {
let mut acc = String::new();
for item in content.lines() {
acc.push_str(
&p.execute_macro(level, "consume", ¯o_name, item)?
.unwrap_or_default(),
);
acc.push_str(&p.state.newline);
}
Some(acc)
} else {
p.execute_macro(level, "consume", ¯o_name, &content)?
};
p.replace_macro(STREAM_CONTAINER, &String::default()); p.state.stream_state.clear();
Ok(result)
}
pub(crate) fn queue_content(
args: &str,
_: usize,
processor: &mut Processor,
) -> RadResult<Option<String>> {
processor.insert_queue(args);
Ok(None)
}
pub(crate) fn if_queue_content(
args: &str,
level: usize,
processor: &mut Processor,
) -> RadResult<Option<String>> {
let mut ap = ArgParser::new().no_strip();
if let Some(args) = ap.args_with_len(args, 2) {
ap.set_strip(true);
let boolean = &processor.parse_and_strip(&mut ap, level, "ifque", &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(),
))
}
}
pub(crate) fn escape_blanks(
_: &str,
level: usize,
processor: &mut Processor,
) -> RadResult<Option<String>> {
if level != 1 {
return Err(RadError::UnallowedMacroExecution(
"\"EB\" is only available on first level".to_string(),
));
}
processor.state.lexor_escape_blanks = true;
Ok(None)
}
#[cfg(not(feature = "wasm"))]
pub(crate) fn read_to(
args: &str,
level: usize,
processor: &mut Processor,
) -> RadResult<Option<String>> {
if !Utils::is_granted("readto", AuthType::FIN, processor)?
|| !Utils::is_granted("readto", AuthType::FOUT, processor)?
{
return Ok(None);
}
let mut ap = ArgParser::new().no_strip();
if let Some(args) = ap.args_with_len(args, 2) {
ap.set_strip(true);
let file_path = PathBuf::from(processor.parse_and_strip(
&mut ap,
level,
"readto",
trim!(&args[0]).as_ref(),
)?);
let to_path = PathBuf::from(processor.parse_and_strip(
&mut ap,
level,
"readto",
trim!(&args[1]).as_ref(),
)?);
if file_path == to_path {
return Err(RadError::InvalidArgument(format!(
"readto cannot read from and into a same file \"{}\"",
file_path.display()
)));
}
let mut raw_include = false;
if file_path.is_file() {
let canonic = file_path.canonicalize()?;
Utils::check_file_sanity(processor, &canonic)?;
if to_path.exists() {
Utils::check_file_sanity(processor, &to_path.canonicalize()?)?;
}
processor.set_sandbox(true);
if args.len() >= 3 {
raw_include = Utils::is_arg_true(&processor.parse_and_strip(
&mut ap,
level,
"readto",
trim!(&args[2]).as_ref(),
)?)?;
if raw_include {
processor.state.flow_control = FlowControl::Escape;
}
}
let file_target = FileTarget::from_path(&to_path)?;
processor.state.relay.push(RelayTarget::File(file_target));
let chunk = processor.process_file_as_chunk(&file_path, ContainerType::Expand)?;
if processor.state.flow_control != FlowControl::None {
processor.reset_flow_control();
}
if raw_include {
processor.state.flow_control = FlowControl::None; }
processor.set_sandbox(false);
processor.state.input_stack.remove(&canonic); processor.state.relay.pop(); Ok(chunk)
} else {
Err(RadError::InvalidArgument(format!(
"readto cannot read non-file \"{}\"",
file_path.display()
)))
}
} else {
Err(RadError::InvalidArgument(
"readto requires two argument".to_owned(),
))
}
}
#[cfg(not(feature = "wasm"))]
pub(crate) fn read_in(
args: &str,
level: usize,
processor: &mut Processor,
) -> RadResult<Option<String>> {
if !Utils::is_granted("readin", AuthType::FIN, processor)? {
return Ok(None);
}
let mut ap = ArgParser::new().no_strip();
if let Some(args) = ap.args_with_len(args, 1) {
ap.set_strip(true);
let file_path = PathBuf::from(processor.parse_and_strip(
&mut ap,
level,
"readin",
trim!(&args[0]).as_ref(),
)?);
let mut raw_include = false;
if file_path.is_file() {
let canonic = file_path.canonicalize()?;
Utils::check_file_sanity(processor, &canonic)?;
processor.set_sandbox(true);
if args.len() >= 2 {
raw_include = Utils::is_arg_true(&processor.parse_and_strip(
&mut ap,
level,
"readin",
trim!(&args[1]).as_ref(),
)?)?;
if raw_include {
processor.state.flow_control = FlowControl::Escape;
}
}
if let Some(relay) = processor.state.relay.last() {
processor.log_warning(
&format!("Read file's content will be relayed to \"{:?}\"", relay),
WarningType::Sanity,
)?;
}
let chunk = processor.process_file(&file_path)?;
if processor.state.flow_control != FlowControl::None {
processor.reset_flow_control();
}
if raw_include {
processor.state.flow_control = FlowControl::None;
}
processor.set_sandbox(false);
processor.state.input_stack.remove(&canonic); Ok(chunk)
} else {
Err(RadError::InvalidArgument(format!(
"readin cannot read non-file \"{}\"",
file_path.display()
)))
}
} else {
Err(RadError::InvalidArgument(
"readin requires an argument".to_owned(),
))
}
}
pub(crate) fn execute_macro(
args: &str,
level: usize,
processor: &mut Processor,
) -> RadResult<Option<String>> {
let mut ap = ArgParser::new().no_strip();
if let Some(args) = ap.args_with_len(args, 2) {
ap.set_strip(true);
let macro_name =
trim!(&processor.parse_and_strip(&mut ap, level, "exec", &args[0])?).to_string();
let args = processor.parse_and_strip(&mut ap, level, "exec", &args[1])?;
let result = processor
.execute_macro(level, "exec", ¯o_name, &args)?
.unwrap_or_default();
Ok(Some(result))
} else {
Err(RadError::InvalidArgument(
"exec requires two argument".to_owned(),
))
}
}
pub(crate) fn spread_data(
args: &str,
level: usize,
processor: &mut Processor,
) -> RadResult<Option<String>> {
let mut ap = ArgParser::new().no_strip();
if let Some(args) = ap.args_with_len(args, 2) {
ap.set_strip(true);
let expanded_name = &processor.parse_and_strip(&mut ap, level, "spread", &args[0])?;
let expanded_data = &processor.parse_and_strip(&mut ap, level, "spread", &args[1])?;
let macro_name = trim!(expanded_name);
let macro_data = trim!(expanded_data);
let result =
Formatter::csv_to_macros(¯o_name, ¯o_data, &processor.state.newline)?;
#[cfg(feature = "debug")]
let original = processor.is_debug();
#[cfg(feature = "debug")]
processor.set_debug(false);
let result = processor.parse_and_strip(&mut ap, level, "spread", &result)?;
#[cfg(feature = "debug")]
{
use crate::debugger::DebugSwitch;
processor.set_debug(original);
match processor.get_debug_switch() {
DebugSwitch::StepMacro | DebugSwitch::NextMacro => {
processor.set_prompt("\"Spread macro\"")
}
_ => (),
}
}
Ok(Some(result))
} else {
Err(RadError::InvalidArgument(
"spread requires two arguments".to_owned(),
))
}
}
pub(crate) fn stream(args_src: &str, _: usize, p: &mut Processor) -> RadResult<Option<String>> {
if p.state.stream_state.on_stream {
return Err(RadError::InvalidArgument(
"Stream cannot be nested".to_string(),
));
}
p.state.stream_state.on_stream = true;
let name = trim!(args_src);
if name.is_empty() {
return Err(RadError::InvalidArgument(
"stream requires an argument ( macro name )".to_owned(),
));
}
p.log_warning("Streaming text content to a macro", WarningType::Security)?;
p.state.stream_state.macro_name_src = name.to_string();
p.add_container_macro(STREAM_CONTAINER)?;
let rtype = RelayTarget::Macro(STREAM_CONTAINER.to_string());
p.state.relay.push(rtype);
Ok(None)
}
pub(crate) fn stream_by_lines(
args_src: &str,
_: usize,
p: &mut Processor,
) -> RadResult<Option<String>> {
if p.state.stream_state.on_stream {
return Err(RadError::InvalidArgument(
"Stream series cannot be nested".to_string(),
));
}
p.state.stream_state.on_stream = true;
p.state.stream_state.as_lines = true;
let name = trim!(args_src);
if name.is_empty() {
return Err(RadError::InvalidArgument(
"streaml requires an argument ( macro name )".to_owned(),
));
}
p.log_warning("Streaming text content to a macro", WarningType::Security)?;
p.state.stream_state.macro_name_src = name.to_string();
p.add_container_macro(STREAM_CONTAINER)?;
let rtype = RelayTarget::Macro(STREAM_CONTAINER.to_string());
p.state.relay.push(rtype);
Ok(None)
}
#[cfg(not(feature = "wasm"))]
pub(crate) fn include(
args: &str,
level: usize,
processor: &mut Processor,
) -> RadResult<Option<String>> {
if !Utils::is_granted("include", AuthType::FIN, processor)? {
return Ok(None);
}
let mut ap = ArgParser::new().no_strip();
let args = ap.args_to_vec(args, ',', SplitVariant::Never);
ap.set_strip(true);
if !args.is_empty() {
let mut file_path = PathBuf::from(
trim!(&processor.parse_and_strip(&mut ap, level, "include", &args[0])?).as_ref(),
);
let mut raw_include = false;
if let ProcessInput::File(path) = &processor.state.current_input {
if file_path.is_relative() {
file_path = path.parent().unwrap().join(file_path);
}
}
if file_path.is_file() {
let canonic = file_path.canonicalize()?;
Utils::check_file_sanity(processor, &canonic)?;
processor.set_sandbox(true);
if args.len() >= 2 {
raw_include = Utils::is_arg_true(
&processor.parse_and_strip(&mut ap, level, "include", &args[1])?,
)?;
if raw_include {
processor.state.flow_control = FlowControl::Escape;
}
}
let container_type = if level != 1 {
ContainerType::Argument
} else {
ContainerType::Expand
};
let chunk = processor.process_file_as_chunk(&file_path, container_type)?;
if processor.state.flow_control != FlowControl::None {
processor.reset_flow_control();
}
if raw_include {
processor.state.flow_control = FlowControl::None;
}
processor.set_sandbox(false);
processor.state.input_stack.remove(&canonic); Ok(chunk)
} else {
let formatted = format!(
"File path : \"{}\" doesn't exist or not a file",
file_path.display()
);
Err(RadError::InvalidArgument(formatted))
}
} else {
Err(RadError::InvalidArgument(
"Include requires an argument".to_owned(),
))
}
}
#[cfg(not(feature = "wasm"))]
pub(crate) fn incread(
args: &str,
level: usize,
processor: &mut Processor,
) -> RadResult<Option<String>> {
if !Utils::is_granted("incread", AuthType::FIN, processor)? {
return Ok(None);
}
if !args.is_empty() {
Ok(processor.execute_macro(level, "incread", "include", args)?)
} else {
Err(RadError::InvalidArgument(
"Include requires an argument".to_owned(),
))
}
}
#[cfg(not(feature = "wasm"))]
pub(crate) fn temp_include(
_: &str,
level: usize,
processor: &mut Processor,
) -> RadResult<Option<String>> {
if !Utils::is_granted("tempin", AuthType::FIN, processor)? {
return Ok(None);
}
let file = processor.get_temp_path().display();
let chunk = Self::include(&file.to_string(), level, processor)?;
Ok(chunk)
}
#[allow(unused_variables)]
#[cfg(debug_assertions)]
#[allow(dead_code)]
pub(crate) fn test_logics(
args: &str,
level: usize,
processor: &mut Processor,
) -> RadResult<Option<String>> {
if let Some(args) = ArgParser::new().no_strip().args_with_len(args, 3) {
Ok(None)
} else {
Err(RadError::InvalidArgument(
"Insufficient argumetns for test".to_owned(),
))
}
}
}