use std::array::IntoIter;
use std::io::Write;
use std::fs::OpenOptions;
use std::collections::HashMap;
use std::iter::FromIterator;
use std::path::PathBuf;
use std::process::Command;
use crate::error::RadError;
use crate::arg_parser::ArgParser;
use crate::consts::MAIN_CALLER;
use regex::Regex;
use crate::utils::Utils;
use crate::processor::{Processor, auth::{AuthState, AuthType}};
#[cfg(feature = "csv")]
use crate::formatter::Formatter;
#[cfg(feature = "lipsum")]
use lipsum::lipsum;
pub type MacroType = fn(&str, bool ,&mut Processor) -> Result<Option<String>, RadError>;
#[derive(Clone)]
pub struct BasicMacro {
macros : HashMap<String, MacroType>,
}
impl BasicMacro {
pub fn empty() -> Self {
Self {
macros: HashMap::new(),
}
}
pub fn new() -> Self {
#[allow(unused_mut)]
let mut map = HashMap::from_iter(IntoIter::new([
("regex".to_owned(), BasicMacro::regex_sub as MacroType),
("trim".to_owned(), BasicMacro::trim as MacroType),
("chomp".to_owned(), BasicMacro::chomp as MacroType).to_owned(),
("comp".to_owned(), BasicMacro::compress as MacroType).to_owned(),
("include".to_owned(), BasicMacro::include as MacroType).to_owned(),
("repeat".to_owned(), BasicMacro::repeat as MacroType).to_owned(),
("syscmd".to_owned(), BasicMacro::syscmd as MacroType).to_owned(),
("if".to_owned(), BasicMacro::if_cond as MacroType).to_owned(),
("ifelse".to_owned(), BasicMacro::ifelse as MacroType).to_owned(),
("ifdef".to_owned(), BasicMacro::ifdef as MacroType).to_owned(),
("foreach".to_owned(), BasicMacro::foreach as MacroType).to_owned(),
("forloop".to_owned(), BasicMacro::forloop as MacroType).to_owned(),
("undef".to_owned(), BasicMacro::undefine_call as MacroType).to_owned(),
("rename".to_owned(), BasicMacro::rename_call as MacroType).to_owned(),
("append".to_owned(), BasicMacro::append as MacroType).to_owned(),
("len".to_owned(), BasicMacro::len as MacroType).to_owned(),
("tr".to_owned(), BasicMacro::translate as MacroType).to_owned(),
("sub".to_owned(), BasicMacro::substring as MacroType).to_owned(),
("pause".to_owned(), BasicMacro::pause as MacroType).to_owned(),
("tempto".to_owned(), BasicMacro::set_temp_target as MacroType).to_owned(),
("tempout".to_owned(), BasicMacro::temp_out as MacroType).to_owned(),
("tempin".to_owned(), BasicMacro::temp_include as MacroType).to_owned(),
("redir".to_owned(), BasicMacro::temp_redirect as MacroType).to_owned(),
("fileout".to_owned(), BasicMacro::file_out as MacroType).to_owned(),
("pipe".to_owned(), BasicMacro::pipe as MacroType).to_owned(),
("bind".to_owned(), BasicMacro::bind_to_local as MacroType).to_owned(),
("env".to_owned(), BasicMacro::get_env as MacroType).to_owned(),
("path".to_owned(), BasicMacro::merge_path as MacroType).to_owned(),
("nl".to_owned(), BasicMacro::newline as MacroType).to_owned(),
("-".to_owned(), BasicMacro::get_pipe as MacroType).to_owned(),
]));
#[cfg(feature = "csv")]
{
map.insert("from".to_owned(), BasicMacro::from_data as MacroType);
map.insert("table".to_owned(), BasicMacro::table as MacroType);
}
#[cfg(feature = "chrono")]
{
map.insert("time".to_owned(), BasicMacro::time as MacroType);
map.insert("date".to_owned(), BasicMacro::date as MacroType);
}
#[cfg(feature = "lipsum")]
map.insert("lipsum".to_owned(), BasicMacro::placeholder as MacroType);
#[cfg(feature = "evalexpr")]
map.insert("eval".to_owned(), BasicMacro::eval as MacroType);
Self { macros : map }
}
pub fn add_new_rule(&mut self, name: &str, macro_ref: MacroType) {
self.macros.insert(name.to_owned(), macro_ref);
}
pub fn contains(&self, name: &str) -> bool {
self.macros.contains_key(name)
}
fn is_granted(name:&str, auth_type: AuthType, processor: &mut Processor) -> Result<bool, RadError> {
match processor.get_auth_state(&auth_type) {
AuthState::Restricted => {
processor.log_error(&format!("\"{}\" not allowed.\"{:?}\" permission is required", name, auth_type))?;
Ok(false)
}
AuthState::Warn => {
processor.log_warning(&format!("\"{}\" was called with \"{:?}\" permission", name, auth_type))?;
Ok(true)
}
AuthState::Open => Ok(true),
}
}
pub fn call(&mut self, name : &str, args: &str,greedy: bool, processor: &mut Processor) -> Result<Option<String>, RadError> {
if let Some(func) = self.macros.get(name) {
let result = func(args, greedy, processor);
if let Err(err) = result {
eprintln!("{}",err);
Ok(None)
} else { result }
} else {
Ok(None)
}
}
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);
}
#[cfg(feature = "chrono")]
fn time(_: &str, _: bool, _ : &mut Processor) -> Result<Option<String>, RadError> {
Ok(Some(format!("{}", chrono::offset::Local::now().format("%H:%M:%S"))))
}
#[cfg(feature = "chrono")]
fn date(_: &str, _: bool, _ : &mut Processor) -> Result<Option<String>, RadError> {
Ok(Some(format!("{}", chrono::offset::Local::now().format("%Y-%m-%d"))))
}
fn regex_sub(args: &str, greedy: bool, _: &mut Processor) -> Result<Option<String>, RadError> {
if let Some(args) = ArgParser::new().args_with_len(args, 3, greedy) {
let source= &args[0];
let match_expr= &args[1];
let substitution= &args[2];
let reg = Regex::new(&format!(r"{}", match_expr))?;
let result = reg.replace_all(source, substitution); Ok(Some(result.to_string()))
} else {
Err(RadError::InvalidArgument("Regex sub requires three arguments".to_owned()))
}
}
#[cfg(feature = "evalexpr")]
fn eval(args: &str, greedy: bool,_: &mut Processor ) -> Result<Option<String>, RadError> {
if let Some(args) = ArgParser::new().args_with_len(args, 1, greedy) {
let formula = &args[0];
let result = evalexpr::eval(formula)?;
Ok(Some(result.to_string()))
} else {
Err(RadError::InvalidArgument("Eval requires an argument".to_owned()))
}
}
fn trim(args: &str, greedy: bool, _: &mut Processor) -> Result<Option<String>, RadError> {
if let Some(args) = ArgParser::new().args_with_len(args, 1, greedy) {
Ok(Some(Utils::trim(&args[0])?))
} else {
Err(RadError::InvalidArgument("Trim requires an argument".to_owned()))
}
}
fn chomp(args: &str, greedy: bool, processor: &mut Processor) -> Result<Option<String>, RadError> {
if let Some(args) = ArgParser::new().args_with_len(args, 1, greedy) {
let source = &args[0];
let reg = Regex::new(&format!(r"{0}\s*{0}", &processor.newline))?;
let result = reg.replace_all(source, &format!("{0}{0}", &processor.newline));
Ok(Some(result.to_string()))
} else {
Err(RadError::InvalidArgument("Chomp requires an argument".to_owned()))
}
}
fn compress(args: &str, greedy: bool, processor: &mut Processor) -> Result<Option<String>, RadError> {
if let Some(args) = ArgParser::new().args_with_len(args, 1, greedy) {
let source = &args[0];
let result = Utils::trim(&BasicMacro::chomp(source,greedy, processor)?.unwrap())?;
Ok(Some(result.to_string()))
} else {
Err(RadError::InvalidArgument("Compress requires an argument".to_owned()))
}
}
#[cfg(feature = "lipsum")]
fn placeholder(args: &str, greedy: bool,_: &mut Processor) -> Result<Option<String>, RadError> {
if let Some(args) = ArgParser::new().args_with_len(args, 1, greedy) {
let word_count = &args[0];
if let Ok(count) = Utils::trim(word_count)?.parse::<usize>() {
Ok(Some(lipsum(count)))
} else {
Err(RadError::InvalidArgument(format!("Lipsum needs a number bigger or equal to 0 (unsigned integer) but given \"{}\"", word_count)))
}
} else {
Err(RadError::InvalidArgument("Placeholder requires an argument".to_owned()))
}
}
fn include(args: &str, greedy: bool, processor: &mut Processor) -> Result<Option<String>, RadError> {
if !Self::is_granted("include", AuthType::FIN,processor)? {
return Ok(None);
}
if let Some(args) = ArgParser::new().args_with_len(args, 1, greedy) {
let raw = Utils::trim(&args[0])?;
let file_path = std::path::Path::new(&raw);
if file_path.is_file() {
processor.set_sandbox();
let result = processor.from_file(file_path)?;
Ok(Some(result))
} 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()))
}
}
fn repeat(args: &str, greedy: bool,_: &mut Processor) -> Result<Option<String>, RadError> {
if let Some(args) = ArgParser::new().args_with_len(args, 2, greedy) {
let repeat_count;
if let Ok(count) = Utils::trim(&args[0])?.parse::<usize>() {
repeat_count = count;
} else {
return Err(RadError::InvalidArgument(format!("Repeat needs a number bigger or equal to 0 (unsigned integer) but given \"{}\"", &args[0])));
}
let repeat_object = &args[1];
let mut repeated = String::new();
for _ in 0..repeat_count {
repeated.push_str(&repeat_object);
}
Ok(Some(repeated))
} else {
Err(RadError::InvalidArgument("Repeat requires two arguments".to_owned()))
}
}
fn syscmd(args: &str, _: bool,p: &mut Processor) -> Result<Option<String>, RadError> {
if !Self::is_granted("syscmd", AuthType::CMD,p)? {
return Ok(None);
}
if let Some(args_content) = ArgParser::new().args_with_len(args, 1, true) {
let source = &args_content[0];
let arg_vec = source.split(' ').collect::<Vec<&str>>();
let output = if cfg!(target_os = "windows") {
Command::new("cmd")
.arg("/C")
.args(arg_vec)
.output()
.expect("failed to execute process")
.stdout
} else {
let sys_args = if arg_vec.len() > 1 { &arg_vec[1..] } else { &[] };
Command::new(&arg_vec[0])
.args(sys_args)
.output()
.expect("failed to execute process")
.stdout
};
Ok(Some(String::from_utf8(output)?))
} else {
Err(RadError::InvalidArgument("Syscmd requires an argument".to_owned()))
}
}
fn if_cond(args: &str, greedy: bool, processor: &mut Processor) -> Result<Option<String>, RadError> {
if let Some(args) = ArgParser::new().args_with_len(args, 2, greedy) {
let boolean = &args[0];
let if_state = &args[1];
let trimmed_cond = Utils::trim(boolean)?;
if let Ok(cond) = trimmed_cond.parse::<bool>() {
if cond {
let result = processor.parse_chunk_args(0, &MAIN_CALLER.to_owned(), if_state)?;
return Ok(Some(result));
}
} else if let Ok(number) = trimmed_cond.parse::<i32>() {
if number != 0 {
let result = processor.parse_chunk_args(0, &MAIN_CALLER.to_owned(), if_state)?;
return Ok(Some(result));
}
} else {
return Err(RadError::InvalidArgument(format!("If requires either true/false or zero/nonzero integer but given \"{}\"", trimmed_cond)))
}
Ok(None)
} else {
Err(RadError::InvalidArgument("if requires two arguments".to_owned()))
}
}
fn ifelse(args: &str, greedy: bool, processor: &mut Processor) -> Result<Option<String>, RadError> {
if let Some(args) = ArgParser::new().args_with_len(args, 3, greedy) {
let boolean = &args[0];
let if_state = &args[1];
let trimmed_cond = Utils::trim(boolean)?;
if let Ok(cond) = trimmed_cond.parse::<bool>() {
if cond {
let result = processor.parse_chunk_args(0, &MAIN_CALLER.to_owned(), if_state)?;
return Ok(Some(result));
}
} else if let Ok(number) = trimmed_cond.parse::<i32>() {
if number != 0 {
let result = processor.parse_chunk_args(0, &MAIN_CALLER.to_owned(), if_state)?;
return Ok(Some(result));
}
} else {
return Err(RadError::InvalidArgument(format!("Ifelse requires either true/false or zero/nonzero integer but given \"{}\"",trimmed_cond)))
}
let else_state = &args[2];
let result = processor.parse_chunk_args(0, &MAIN_CALLER.to_owned(), else_state)?;
return Ok(Some(result));
} else {
Err(RadError::InvalidArgument("ifelse requires three argument".to_owned()))
}
}
fn ifdef(args: &str, greedy: bool, processor: &mut Processor) -> Result<Option<String>, RadError> {
if let Some(args) = ArgParser::new().args_with_len(args, 1, greedy) {
let name = &Utils::trim(&args[0])?;
let map = processor.get_map();
if map.basic.contains(name) || map.custom.contains_key(name) {
Ok(Some("true".to_owned()))
} else {
Ok(Some("false".to_owned()))
}
} else {
Err(RadError::InvalidArgument("Ifdef requires an argument".to_owned()))
}
}
fn undefine_call(args: &str, greedy: bool, processor: &mut Processor) -> Result<Option<String>, RadError> {
if let Some(args) = ArgParser::new().args_with_len(args, 1, greedy) {
let name = &Utils::trim(&args[0])?;
let map = processor.get_map();
if map.contains(name) {
map.undefine(name);
} else {
processor.log_error(&format!("Macro \"{}\" doesn't exist, therefore cannot undefine", name))?;
}
Ok(None)
} else {
Err(RadError::InvalidArgument("Undefine requires an argument".to_owned()))
}
}
fn foreach(args: &str, greedy: bool, processor: &mut Processor) -> Result<Option<String>, RadError> {
if let Some(args) = ArgParser::new().args_with_len(args, 2, greedy) {
let mut sums = String::new();
let target = &args[1]; let loopable = &args[0];
for value in loopable.split(',') {
let result = processor.parse_chunk_args(0, &MAIN_CALLER.to_owned(), &target.replace("$:", value))?;
sums.push_str(&result);
}
Ok(Some(sums))
} else {
Err(RadError::InvalidArgument("Foreach requires two argument".to_owned()))
}
}
fn forloop(args: &str, greedy: bool, processor: &mut Processor) -> Result<Option<String>, RadError> {
if let Some(args) = ArgParser::new().args_with_len(args, 3, greedy) {
let mut sums = String::new();
let expression = &args[2];
let min: usize;
let max: usize;
if let Ok(num) = Utils::trim(&args[0])?.parse::<usize>() {
min = num;
} else { return Err(RadError::InvalidArgument(format!("Forloop's min value should be non zero positive integer but given {}", &args[0]))); }
if let Ok(num) = Utils::trim(&args[1])?.parse::<usize>() {
max = num
} else { return Err(RadError::InvalidArgument(format!("Forloop's min value should be non zero positive integer gut given \"{}\"", &args[1]))); }
for value in min..=max {
let result = processor.parse_chunk_args(0, &MAIN_CALLER.to_owned(), &expression.replace("$:", &value.to_string()))?;
sums.push_str(&result);
}
Ok(Some(sums))
} else {
Err(RadError::InvalidArgument("Forloop requires two argument".to_owned()))
}
}
#[cfg(feature = "csv")]
fn from_data(args: &str, greedy: bool, processor: &mut Processor) -> Result<Option<String>, RadError> {
if let Some(args) = ArgParser::new().args_with_len(args, 2, greedy) {
let macro_data = &args[0];
let macro_name = &Utils::trim(&args[1])?;
let result = Formatter::csv_to_macros(macro_name, macro_data, &processor.newline)?;
let result = processor.parse_chunk_args(0, &MAIN_CALLER.to_owned(), &result)?;
Ok(Some(result))
} else {
Err(RadError::InvalidArgument("From requires two arguments".to_owned()))
}
}
#[cfg(feature = "csv")]
fn table(args: &str, greedy: bool, p: &mut Processor) -> Result<Option<String>, RadError> {
if let Some(args) = ArgParser::new().args_with_len(args, 2, greedy) {
let table_format = &args[0]; let csv_content = &args[1];
let result = Formatter::csv_to_table(table_format, csv_content, &p.newline)?;
Ok(Some(result))
} else {
Err(RadError::InvalidArgument("Table requires two arguments".to_owned()))
}
}
fn pipe(args: &str, greedy: bool, processor: &mut Processor) -> Result<Option<String>, RadError> {
if let Some(args) = ArgParser::new().args_with_len(args, 1, greedy) {
processor.pipe_value = args[0].to_owned();
}
Ok(None)
}
fn bind_to_local(args: &str, greedy: bool, processor: &mut Processor) -> Result<Option<String>, RadError> {
if let Some(args) = ArgParser::new().args_with_len(args, 2, greedy) {
let name = &args[0];
let value = &args[1];
processor.get_map().new_local(1, name, value);
if processor.get_map().contains(name) {
processor.log_warning(&format!("Creating a binding with a name already existing : \"{}\"", name))?;
}
}
Ok(None)
}
fn get_env(args: &str, _: bool, p : &mut Processor) -> Result<Option<String>, RadError> {
if !Self::is_granted("env", AuthType::ENV,p)? {
return Ok(None);
}
let out = std::env::var(args)?;
Ok(Some(out))
}
fn merge_path(args: &str, greedy: bool, _: &mut Processor) -> Result<Option<String>, RadError> {
if let Some(args) = ArgParser::new().args_with_len(args, 2, greedy) {
let target = Utils::trim(&args[0])?;
let added = Utils::trim(&args[1])?;
let out = format!("{}",&std::path::Path::new(&target).join(&added).display());
Ok(Some(out))
} else {
Err(RadError::InvalidArgument("Path macro needs two arguments".to_owned()))
}
}
fn newline(_: &str, _: bool, p: &mut Processor) -> Result<Option<String>, RadError> {
Ok(Some(p.newline.to_owned()))
}
fn get_pipe(_: &str, _: bool, processor: &mut Processor) -> Result<Option<String>, RadError> {
let out = processor.pipe_value.clone();
processor.pipe_value.clear();
Ok(Some(out))
}
fn len(args: &str, _: bool, _: &mut Processor) -> Result<Option<String>, RadError> {
Ok(Some(args.chars().count().to_string()))
}
fn rename_call(args: &str, greedy: bool, processor: &mut Processor) -> Result<Option<String>, RadError> {
if let Some(args) = ArgParser::new().args_with_len(args, 2, greedy) {
let target = &args[0];
let new = &args[1];
let map = processor.get_map();
if map.contains(target) {
processor.get_map().rename(target, new);
} else {
processor.log_error(&format!("Macro \"{}\" doesn't exist, therefore cannot rename", target))?;
}
Ok(None)
} else {
Err(RadError::InvalidArgument("Rename requires two arguments".to_owned()))
}
}
fn append(args: &str, greedy: bool, processor: &mut Processor) -> Result<Option<String>, RadError> {
if let Some(args) = ArgParser::new().args_with_len(args, 2, greedy) {
let name = &args[0];
let target = &args[1];
let map = processor.get_map();
if map.custom.contains_key(name) {
map.append(name, target);
} else {
processor.log_error(&format!("Macro \"{}\" doesn't exist", name))?;
}
Ok(None)
} else {
Err(RadError::InvalidArgument("Append requires two arguments".to_owned()))
}
}
fn translate(args: &str, greedy: bool, _: &mut Processor) -> Result<Option<String>, RadError> {
if let Some(args) = ArgParser::new().args_with_len(args, 3, greedy) {
let mut source = args[0].clone();
let target = &args[1].chars().collect::<Vec<char>>();
let destination = &args[2].chars().collect::<Vec<char>>();
if target.len() != destination.len() {
return Err(RadError::InvalidArgument(format!("Tr's replacment should have same length of texts while given \"{:?}\" and \"{:?}\"", target, destination)));
}
for i in 0..target.len() {
source = source.replace(target[i], &destination[i].to_string());
}
Ok(Some(source))
} else {
Err(RadError::InvalidArgument("Tr requires three arguments".to_owned()))
}
}
fn substring(args: &str, greedy: bool, _: &mut Processor) -> Result<Option<String>, RadError> {
if let Some(args) = ArgParser::new().args_with_len(args, 3, greedy) {
let source = &args[2];
let mut min: Option<usize> = None;
let mut max: Option<usize> = None;
let start = Utils::trim(&args[0])?;
let end = Utils::trim(&args[1])?;
if let Ok(num) = start.parse::<usize>() {
min.replace(num);
} else {
if start.len() != 0 {
return Err(RadError::InvalidArgument(format!("Sub's min value should be non zero positive integer or empty value but given \"{}\"", start)));
}
}
if let Ok(num) = end.parse::<usize>() {
max.replace(num);
} else {
if end.len() != 0 {
return Err(RadError::InvalidArgument(format!("Sub's max value should be non zero positive integer or empty value but given \"{}\"", end)));
}
}
Ok(Some(Utils::utf8_substring(source, min, max)))
} else {
Err(RadError::InvalidArgument("Sub requires three arguments".to_owned()))
}
}
fn pause(args: &str, greedy: bool, processor : &mut Processor) -> Result<Option<String>, RadError> {
if let Some(args) = ArgParser::new().args_with_len(args, 1, greedy) {
let arg = &args[0];
if let Ok(value) =Utils::is_arg_true(arg) {
if value {
processor.paused = true;
} else {
processor.paused = false;
}
Ok(None)
}
else {
Err(RadError::InvalidArgument(format!("Pause requires either true/false or zero/nonzero integer, but given \"{}\"", arg)))
}
} else {
Err(RadError::InvalidArgument("Pause requires an argument".to_owned()))
}
}
fn temp_out(args: &str, greedy: bool, p: &mut Processor) -> Result<Option<String>, RadError> {
if !Self::is_granted("temput", AuthType::FOUT,p)? {
return Ok(None);
}
if let Some(args) = ArgParser::new().args_with_len(args, 1, greedy) {
let content = &args[0];
p.get_temp_file().write_all(content.as_bytes())?;
Ok(None)
} else {
Err(RadError::InvalidArgument("Tempout requires an argument".to_owned()))
}
}
fn file_out(args: &str, greedy: bool, p: &mut Processor) -> Result<Option<String>, RadError> {
if !Self::is_granted("fileout", AuthType::FOUT,p)? {
return Ok(None);
}
if let Some(args) = ArgParser::new().args_with_len(args, 3, greedy) {
let truncate = &args[0];
let file_name = &args[1];
let content = &args[2];
if let Ok(truncate) = Utils::is_arg_true(truncate) {
let file = std::env::current_dir()?.join(file_name);
let mut target_file;
if truncate {
target_file = OpenOptions::new()
.create(true)
.write(true)
.truncate(true)
.open(file)
.unwrap();
} else {
if !file.is_file() {
return Err(RadError::InvalidArgument(format!("Failed to read \"{}\". Fileout without truncate option needs exsiting file",file.display())));
}
target_file = OpenOptions::new()
.append(true)
.open(file)
.unwrap();
}
target_file.write_all(content.as_bytes())?;
Ok(None)
} else {
Err(RadError::InvalidArgument(format!("Fileout requires either true/false or zero/nonzero integer but given \"{}\"", truncate)))
}
} else {
Err(RadError::InvalidArgument("Fileout requires three argument".to_owned()))
}
}
fn temp_include(_: &str, _: bool, processor: &mut Processor) -> Result<Option<String>, RadError> {
if !Self::is_granted("tempin", AuthType::FIN,processor)? {
return Ok(None);
}
let file = processor.get_temp_path().to_owned();
processor.set_sandbox();
Ok(Some(processor.from_file(&file)?))
}
fn temp_redirect(args: &str, _: bool, p: &mut Processor) -> Result<Option<String>, RadError> {
if !Self::is_granted("redir", AuthType::FOUT,p)? {
return Ok(None);
}
if let Some(args) = ArgParser::new().args_with_len(args, 1, false) {
let toggle = if let Ok(toggle) =Utils::is_arg_true(&args[0]) {
toggle
} else {
return Err(RadError::InvalidArgument(format!("Redir's agument should be valid boolean value but given \"{}\"", &args[0])));
};
p.redirect = toggle;
Ok(None)
} else {
Err(RadError::InvalidArgument("Redir requires an argument".to_owned()))
}
}
fn set_temp_target(args: &str, greedy: bool, processor: &mut Processor) -> Result<Option<String>, RadError> {
if !Self::is_granted("tempto", AuthType::FOUT,processor)? {
return Ok(None);
}
if let Some(args) = ArgParser::new().args_with_len(args, 1, greedy) {
processor.set_temp_file(&PathBuf::from(std::env::temp_dir()).join(&args[0]));
Ok(None)
} else {
Err(RadError::InvalidArgument("Temp requires an argument".to_owned()))
}
}
}