use auth::{AuthType, AuthFlags, AuthState};
#[cfg(feature = "debug")]
use std::io::Read;
use std::io::{self, BufReader, Write};
use std::collections::HashMap;
use std::fs::{File, OpenOptions};
use std::path::{ Path , PathBuf};
use crate::basic::MacroType;
use crate::error::RadError;
use crate::logger::{Logger, LoggerLines};
#[cfg(feature = "debug")]
use crate::logger::DebugSwitch;
use crate::models::{MacroMap, MacroRule, RuleFile, UnbalancedChecker, WriteOption};
use crate::utils::Utils;
use crate::consts::*;
use crate::lexor::*;
use crate::arg_parser::{ArgParser, GreedyState};
pub struct Processor{
current_input : String, auth_flags: AuthFlags,
map: MacroMap,
define_parse: DefineParser,
write_option: WriteOption,
logger: Logger,
checker: UnbalancedChecker,
pub(crate) pipe_value: String,
pub(crate) newline: String,
pub(crate) paused: bool,
pub(crate) redirect: bool,
#[cfg(feature = "debug")]
pub(crate) debug: bool,
#[cfg(feature = "debug")]
pub(crate) debug_log: bool,
#[cfg(feature = "debug")]
debug_switch : DebugSwitch,
#[cfg(feature = "debug")]
line_number : usize,
#[cfg(feature = "debug")]
pub(crate) line_caches: HashMap<usize, String>,
sandbox: bool,
purge: bool,
strict: bool,
always_greedy: bool,
temp_target: (PathBuf,File),
}
impl Processor {
pub fn new() -> Self {
Self::new_processor(true)
}
pub fn empty() -> Self {
Self::new_processor(false)
}
fn new_processor(use_basic: bool) -> Self {
let temp_path= std::env::temp_dir().join("rad.txt");
let temp_target = (
temp_path.to_owned(),
OpenOptions::new()
.create(true)
.write(true)
.truncate(true)
.open(&temp_path)
.unwrap()
);
let mut logger = Logger::new();
logger.set_write_options(Some(WriteOption::Stdout));
let map = if use_basic {
MacroMap::new()
} else {
MacroMap::empty()
};
Self {
current_input: String::from("stdin"),
auth_flags: AuthFlags::new(),
map,
write_option: WriteOption::Stdout,
define_parse: DefineParser::new(),
logger,
checker : UnbalancedChecker::new(),
newline : LINE_ENDING.to_owned(),
pipe_value: String::new(),
paused: false,
redirect: false,
purge: false,
strict: false,
sandbox : false,
#[cfg(feature = "debug")]
debug: false,
#[cfg(feature = "debug")]
debug_log: false,
#[cfg(feature = "debug")]
debug_switch: DebugSwitch::NextLine,
#[cfg(feature = "debug")]
line_number: 1,
#[cfg(feature = "debug")]
line_caches: HashMap::new(),
always_greedy: false,
temp_target,
}
}
pub fn write_to_file(&mut self, target_file: Option<impl AsRef<Path>>) -> Result<&mut Self, RadError> {
if let Some(target_file) = target_file {
let target_file = OpenOptions::new()
.create(true)
.write(true)
.truncate(true)
.open(target_file)?;
self.write_option = WriteOption::File(target_file);
}
Ok(self)
}
pub fn error_to_file(&mut self, target_file: Option<impl AsRef<Path>>) -> Result<&mut Self, RadError> {
if let Some(target_file) = target_file {
let target_file = OpenOptions::new()
.create(true)
.write(true)
.truncate(true)
.open(target_file)?;
self.logger = Logger::new();
self.logger.set_write_options(Some(WriteOption::File(target_file)));
}
Ok(self)
}
pub fn unix_new_line(&mut self, use_unix_new_line: bool) -> &mut Self {
if use_unix_new_line {
self.newline = "\n".to_owned();
}
self
}
pub fn greedy(&mut self, greedy: bool) -> &mut Self {
if greedy {
self.always_greedy = true;
}
self
}
pub fn purge(&mut self, purge: bool) -> &mut Self {
if purge {
self.purge = true;
self.strict = false;
}
self
}
pub fn strict(&mut self, strict: bool) -> &mut Self {
if strict {
self.strict = true;
self.purge = false;
}
self
}
pub fn silent(&mut self, silent: bool) -> &mut Self {
if silent {
self.logger = Logger::new();
self.logger.set_write_options(None);
}
self
}
#[cfg(feature = "debug")]
pub fn debug(&mut self, debug: bool) -> &mut Self {
if debug {
self.debug = true;
}
self
}
#[cfg(feature = "debug")]
pub fn log(&mut self, log: bool) -> &mut Self {
if log {
self.debug_log = true;
}
self
}
#[cfg(feature = "debug")]
pub fn interactive(&mut self, interactive: bool) -> &mut Self {
if interactive {
self.logger.set_debug_interactive();
}
self
}
pub fn custom_rules(&mut self, paths: Option<Vec<impl AsRef<Path>>>) -> Result<&mut Self, RadError> {
if let Some(paths) = paths {
let mut rule_file = RuleFile::new(None);
for p in paths.iter() {
rule_file.melt(p.as_ref())?;
}
self.map.custom.extend(rule_file.rules);
}
Ok(self)
}
pub fn allow(&mut self, auth_types : Option<Vec<AuthType>>) -> &mut Self {
if let Some(auth_types) = auth_types {
for auth in auth_types {
self.auth_flags.set_state(&auth, AuthState::Open)
}
}
self
}
pub fn allow_with_warning(&mut self, auth_types : Option<Vec<AuthType>>) -> &mut Self {
if let Some(auth_types) = auth_types {
for auth in auth_types {
self.auth_flags.set_state(&auth, AuthState::Warn)
}
}
self
}
pub fn discard(&mut self, discard: bool) -> &mut Self {
if discard {
self.write_option = WriteOption::Discard;
}
self
}
pub fn build(&mut self) -> Self {
std::mem::replace(self, Processor::new())
}
#[allow(dead_code)]
pub fn print_result(&mut self) -> Result<(), RadError> {
self.logger.print_result()?;
Ok(())
}
pub fn freeze_to_file(&self, path: impl AsRef<Path>) -> Result<(), RadError> {
RuleFile::new(Some(self.map.custom.clone())).freeze(path.as_ref())?;
Ok(())
}
pub fn add_basic_rules(&mut self, basic_rules:Vec<(&str,MacroType)>) {
for (name, macro_ref) in basic_rules {
self.map.basic.add_new_rule(name, macro_ref);
}
}
pub fn add_custom_rules(&mut self, rules: Vec<(&str,&str,&str)>) {
for (name,args,body) in rules {
self.map.custom.insert(
name.to_owned(),
MacroRule {
name: name.to_owned(),
args: args.split(' ').map(|s| s.to_owned()).collect::<Vec<String>>(),
body: body.to_owned()
}
);
}
}
pub fn from_string(&mut self, content: &str) -> Result<String, RadError> {
self.set_input("String")?;
let mut reader = content.as_bytes();
self.from_buffer(&mut reader)
}
pub fn from_stdin(&mut self) -> Result<String, RadError> {
let stdin = io::stdin();
#[cfg(feature = "debug")]
if self.debug {
let mut input = String::new();
stdin.lock().read_to_string(&mut input)?;
return self.from_buffer(&mut input.as_bytes());
}
let mut reader = stdin.lock();
self.from_buffer(&mut reader)
}
pub fn from_file(&mut self, path :impl AsRef<Path>) -> Result<String, RadError> {
self.set_file(path.as_ref().to_str().unwrap())?;
let file_stream = File::open(path)?;
let mut reader = BufReader::new(file_stream);
self.from_buffer(&mut reader)
}
fn from_buffer(&mut self,buffer: &mut impl std::io::BufRead) -> Result<String, RadError> {
let backup = if self.sandbox { Some(self.backup()) } else { None };
let mut line_iter = Utils::full_lines(buffer).peekable();
let mut lexor = Lexor::new();
let mut frag = MacroFragment::new();
let mut content = String::new();
let mut container = if self.sandbox { Some(&mut content) } else { None };
#[cfg(feature = "debug")]
self.user_input_on_start()?;
loop {
#[cfg(feature = "debug")]
if let Some(line) = line_iter.peek() {
let line = line.as_ref().unwrap();
self.line_caches.insert(self.line_number, line.lines().next().unwrap().to_owned());
self.user_input_on_line(&frag)?;
}
let result = self.parse_line(&mut line_iter, &mut lexor ,&mut frag)?;
match result {
ParseResult::Printable(remainder) => {
self.write_to(&remainder, &mut container)?;
#[cfg(feature = "debug")]
self.line_caches.clear();
if &frag.whole_string != "" {
frag = MacroFragment::new();
}
}
ParseResult::FoundMacro(remainder) => {
self.write_to(&remainder, &mut container)?;
}
ParseResult::NoPrint => { }
ParseResult::EOI => break,
}
#[cfg(feature = "debug")]
{
self.line_number = self.line_number + 1;
}
}
if let Some(backup) = backup { self.recover(backup); self.sandbox = false; }
Ok(content)
}
#[cfg(feature = "debug")]
fn is_local(&self, mut level: usize, name: &str) -> bool {
while level > 0 {
if self.map.local.contains_key(&Utils::local_name(level, &name)) {
return true;
}
level = level - 1;
}
false
}
#[cfg(feature = "debug")]
fn break_point(&mut self, frag: &mut MacroFragment) -> Result<(), RadError> {
if &frag.name == "BR" {
if self.debug {
if let DebugSwitch::NextBreakPoint(name) = &self.debug_switch {
if name == &frag.args || name == "" {
self.debug_switch = DebugSwitch::NextLine;
}
}
frag.clear();
return Ok(());
}
self.logger.wlog("Breakpoint in non debug mode")?;
frag.clear();
}
Ok(())
}
#[cfg(feature = "debug")]
fn user_input_on_start(&mut self) -> Result<(), RadError> {
if self.debug {
let mut log = "Default is next. Ctrl + c to exit.".to_owned();
let mut prompt = self.current_input.as_str();
let mut do_continue = true;
while do_continue {
let input = self.debug_wait_input(&log, Some(prompt))?;
let input = input.lines().next().unwrap();
do_continue = self.parse_debug_command_and_continue(&input, None, &mut log)?;
prompt = "output";
}
}
Ok(())
}
#[cfg(feature = "debug")]
fn user_input_prompt(&mut self, frag: &MacroFragment, initial_prompt: &str) -> Result<(), RadError> {
let mut do_continue = true;
let mut log = match &self.debug_switch {
&DebugSwitch::NextMacro | &DebugSwitch::StepMacro => {
self.line_caches.get(&self.logger.get_abs_last_line()).unwrap().to_owned()
}
_ => {
self.line_caches.get(&self.line_number).unwrap().to_owned()
}
};
let mut prompt = initial_prompt;
while do_continue {
let input = self.debug_wait_input(
&log,
Some(prompt)
)?;
let input = input.lines().next().unwrap();
do_continue = self.parse_debug_command_and_continue(&input, Some(frag),&mut log)?;
prompt = "output";
}
Ok(())
}
#[cfg(feature = "debug")]
fn user_input_on_line(&mut self,frag: &MacroFragment) -> Result<(), RadError> {
if self.debug {
if let DebugSwitch::NextLine = self.debug_switch {
} else {
return Ok(()); }
self.user_input_prompt(frag, "line")?;
}
Ok(())
}
#[cfg(feature = "debug")]
fn user_input_before_macro(&mut self, frag: &MacroFragment) -> Result<(), RadError> {
if self.debug {
match &self.debug_switch {
&DebugSwitch::UntilMacro => (),
_ => return Ok(()),
}
self.user_input_prompt(frag, "until")?;
}
Ok(())
}
#[cfg(feature = "debug")]
fn user_input_on_macro(&mut self, frag: &MacroFragment) -> Result<(), RadError> {
if self.debug {
match &self.debug_switch {
&DebugSwitch::NextMacro | &DebugSwitch::StepMacro => (),
_ => return Ok(()),
}
self.user_input_prompt(frag, "macro")?;
}
Ok(())
}
#[cfg(feature = "debug")]
fn user_input_on_step(&mut self, frag: &MacroFragment) -> Result<(), RadError> {
if self.debug {
if let &DebugSwitch::StepMacro = &self.debug_switch {
} else {
return Ok(()); }
self.user_input_prompt(frag, "step")?;
}
Ok(())
}
#[cfg(feature = "debug")]
fn parse_debug_command_and_continue(&mut self, command_input: &str, frag: Option<&MacroFragment>, log: &mut String) -> Result<bool, RadError> {
let command_input: Vec<&str> = command_input.split(' ').collect();
let command = command_input[0];
let command_args = if command_input.len() == 2 {command_input[1]} else { "" };
match command.to_lowercase().as_str() {
"cl" | "clear" => {
Utils::clear_terminal()?;
return Ok(true);
}
"c" | "continue" => {
self.debug_switch = DebugSwitch::NextBreakPoint(command_args.to_owned());
}
"n" | "next" | "" => {
self.debug_switch = DebugSwitch::NextLine;
}
"m" | "macro" => {
self.debug_switch = DebugSwitch::NextMacro;
}
"u" | "until" => {
self.debug_switch = DebugSwitch::UntilMacro;
}
"s" | "step" => {
self.debug_switch = DebugSwitch::StepMacro;
}
"h" | "help" => {
*log = RDB_HELP.to_owned();
return Ok(true);
}
"p" | "print" => {
if let Some(frag) = frag {
match command_args.to_lowercase().as_str() {
"name" | "n" => {
*log = frag.name.to_owned();
}
"line" | "l" => {
match &self.debug_switch{
DebugSwitch::StepMacro | DebugSwitch::NextMacro => {
*log = self.logger.get_abs_last_line().to_string();
}
_ => {
*log = self.line_number.to_string();
}
}
}
"span" | "s" => {
let mut line_number = match &self.debug_switch {
&DebugSwitch::NextMacro | &DebugSwitch::StepMacro => {
self.logger.get_abs_line()
}
_ => self.line_number
};
let mut sums = String::new();
while let Some(line) = self.line_caches.get(&line_number) {
let mut this_line = format!("{}{}",LINE_ENDING,line);
this_line.push_str(&sums);
sums = this_line;
line_number = line_number - 1;
}
*log = sums;
}
"text" | "t" => {
match &self.debug_switch{
DebugSwitch::StepMacro | DebugSwitch::NextMacro => {
*log = self.line_caches.get(&self.logger.get_abs_last_line()).unwrap().to_owned();
}
_ => {
*log = self.line_caches.get(&self.line_number).unwrap().to_owned();
}
}
}
"arg" | "a" => {
*log = frag.args.to_owned();
}
_ => {
*log = format!("Invalid argument \"{}\"",&command_args);
}
} } else { return Ok(false);
}
return Ok(true);
} _ => {
*log = format!("Invalid command : {} {}",command, &command_args);
return Ok(true);
},
}
Ok(false)
}
#[cfg(feature = "debug")]
pub(crate) fn debug_wait_input(&self, log: &str, prompt: Option<&str>) -> Result<String, RadError> {
Ok(self.logger.dlog_command(log, prompt)?)
}
#[cfg(feature = "debug")]
pub(crate) fn debug_print_log(&self,log : &str) -> Result<(), RadError> {
self.logger.dlog_print(log)?;
Ok(())
}
fn parse_line(&mut self, lines :&mut impl std::iter::Iterator<Item = std::io::Result<String>>, lexor : &mut Lexor ,frag : &mut MacroFragment) -> Result<ParseResult, RadError> {
self.logger.add_line_number();
if let Some(line) = lines.next() {
let line = line?;
let remainder = self.parse(lexor, frag, &line, 0, MAIN_CALLER)?;
self.map.clear_local();
if remainder.len() != 0 {
if !frag.is_empty() {
Ok(ParseResult::FoundMacro(remainder))
}
else {
Ok(ParseResult::Printable(remainder))
}
}
else {
Ok(ParseResult::NoPrint)
}
} else {
Ok(ParseResult::EOI)
}
}
pub(crate) fn parse_chunk_args(&mut self, level: usize, _caller: &str, chunk: &str) -> Result<String, RadError> {
let mut lexor = Lexor::new();
let mut frag = MacroFragment::new();
let mut result = String::new();
let backup = self.logger.backup_lines();
self.logger.set_chunk(true);
for line in Utils::full_lines(chunk.as_bytes()) {
let line = line?;
result.push_str(&self.parse(&mut lexor, &mut frag, &line, level, "")?);
self.logger.add_line_number();
}
self.logger.set_chunk(false);
self.logger.recover_lines(backup);
return Ok(result);
}
fn parse_chunk_body(&mut self, level: usize, caller: &str, chunk: &str) -> Result<String, RadError> {
let mut lexor = Lexor::new();
let mut frag = MacroFragment::new();
let backup = self.logger.backup_lines();
let result = self.parse(&mut lexor, &mut frag, &chunk, level, caller)?;
self.logger.recover_lines(backup);
return Ok(result);
}
fn parse(&mut self,lexor: &mut Lexor, frag: &mut MacroFragment, line: &str, level: usize, caller: &str) -> Result<String, RadError> {
self.logger.reset_char_number();
let mut remainder = String::new();
lexor.escape_nl = false;
for ch in line.chars() {
self.logger.add_char_number();
let lex_result = lexor.lex(ch)?;
match lex_result {
LexResult::Discard => (),
LexResult::Ignore => frag.whole_string.push(ch),
LexResult::Literal(cursor) => {
self.lex_branch_literal(ch, frag, &mut remainder, cursor);
}
LexResult::StartFrag => {
self.lex_branch_start_frag(ch, frag, &mut remainder, lexor)?;
},
LexResult::RestartName => {
remainder.push_str(&frag.whole_string);
frag.clear();
frag.whole_string.push('$');
},
LexResult::EmptyName => {
self.lex_branch_empty_name(ch, frag, &mut remainder, lexor);
}
LexResult::AddToRemainder => {
self.lex_branch_add_to_remainder(ch, &mut remainder)?;
}
LexResult::AddToFrag(cursor) => {
self.lex_branch_add_to_frag(ch, frag, cursor);
}
LexResult::EndFrag => {
self.lex_branch_end_frag(ch,frag,&mut remainder, lexor, level, caller)?;
}
LexResult::ExitFrag => {
self.lex_branch_exit_frag(ch,frag,&mut remainder);
}
}
} Ok(remainder)
}
fn evaluate(&mut self,level: usize, caller: &str, name: &str, raw_args: &str, greedy: bool) -> Result<EvalResult, RadError> {
let level = level + 1;
let args = self.parse_chunk_args(level, name, raw_args)?;
#[cfg(feature = "debug")]
if self.debug_log {
self.debug_print_log(
&format!(
"Level = \"{}\"{}Name = \"{}\"{}Args = \"{}\"{}",
level,
LINE_ENDING,
name,
LINE_ENDING,
raw_args,
LINE_ENDING,
)
)?;
}
if caller == name {
self.log_warning(&format!("Calling self, which is \"{}\", can possibly trigger infinite loop", name))?;
}
let mut temp_level = level;
while temp_level > 0 {
if let Some(local) = self.map.local.get(&Utils::local_name(temp_level, &name)) {
return Ok(EvalResult::Eval(Some(local.to_owned())));
}
temp_level = temp_level - 1;
}
if self.map.custom.contains_key(name) {
if let Some(result) = self.invoke_rule(level, name, &args, greedy)? {
return Ok(EvalResult::Eval(Some(result)));
} else {
return Ok(EvalResult::None);
}
}
else if self.map.basic.contains(&name) {
let final_result = self.map.basic.clone().call(name, &args, greedy, self)?;
return Ok(EvalResult::Eval(final_result));
}
else {
return Ok(EvalResult::None);
}
}
fn invoke_rule(&mut self,level: usize ,name: &str, arg_values: &str, greedy: bool) -> Result<Option<String>, RadError> {
let rule = self.map.custom.get(name).unwrap().clone();
let arg_types = &rule.args;
let args: Vec<String>;
if let Some(content) = ArgParser::new().args_with_len(arg_values, arg_types.len(), greedy) {
args = content;
} else {
self.log_error(&format!("{}'s arguments are not sufficient. Given {}, but needs {}", name, ArgParser::new().args_to_vec(arg_values, ',', GreedyState::Never).len(), arg_types.len()))?;
return Ok(None);
}
for (idx, arg_type) in arg_types.iter().enumerate() {
self.map.new_local(level + 1, arg_type ,&args[idx]);
}
let result = self.parse_chunk_body(level, &name, &rule.body)?;
Ok(Some(result))
}
fn add_rule(&mut self, frag: &MacroFragment, remainder: &mut String) -> Result<(), RadError> {
if let Some((name,args,body)) = self.define_parse.parse_define(&frag.args) {
self.map.register(&name, &args, &body)?;
} else {
self.log_error(&format!(
"Failed to register a macro : \"{}\"",
frag.args.split(',').collect::<Vec<&str>>()[0]
))?;
remainder.push_str(&frag.whole_string);
}
Ok(())
}
fn write_to(&mut self, content: &str, container: &mut Option<&mut String>) -> Result<(), RadError> {
if content.len() == 0 { return Ok(()); }
if let Some(container) = container {
container.push_str(content);
}
else {
if self.redirect {
self.temp_target.1.write(content.as_bytes())?;
} else {
match &mut self.write_option {
WriteOption::File(f) => f.write_all(content.as_bytes())?,
WriteOption::Stdout => print!("{}", content),
WriteOption::Discard => () }
}
}
Ok(())
}
fn lex_branch_literal(&mut self, ch: char,frag: &mut MacroFragment, remainder: &mut String, cursor: Cursor) {
match cursor {
Cursor::Name => {
frag.whole_string.push(ch);
remainder.push_str(&frag.whole_string);
frag.clear();
}
Cursor::None => { remainder.push(ch); }
Cursor::Arg => {
frag.args.push(ch);
frag.whole_string.push(ch);
}
}
}
fn lex_branch_start_frag(&mut self, ch: char,frag: &mut MacroFragment, remainder: &mut String, lexor : &mut Lexor) -> Result<(), RadError> {
#[cfg(feature = "debug")]
self.user_input_before_macro(&frag)?;
frag.whole_string.push(ch);
if self.paused && frag.name != "pause" {
lexor.reset();
remainder.push_str(&frag.whole_string);
frag.clear();
}
Ok(())
}
fn lex_branch_empty_name(&mut self, ch: char,frag: &mut MacroFragment, remainder: &mut String, lexor : &mut Lexor) {
frag.whole_string.push(ch);
self.logger.freeze_number();
if self.paused {
lexor.reset();
remainder.push_str(&frag.whole_string);
frag.clear();
}
}
fn lex_branch_add_to_remainder(&mut self, ch: char,remainder: &mut String) -> Result<(), RadError> {
if !self.checker.check(ch) && !self.paused {
self.logger.freeze_number();
self.log_warning("Unbalanced parenthesis detected.")?;
}
remainder.push(ch);
Ok(())
}
fn lex_branch_add_to_frag(&mut self, ch: char,frag: &mut MacroFragment, cursor: Cursor) {
match cursor{
Cursor::Name => {
if frag.name.len() == 0 {
self.logger.freeze_number();
}
match ch {
'|' => frag.pipe = true,
'+' => frag.greedy = true,
'*' => frag.yield_literal = true,
'^' => frag.trimmed = true,
_ => frag.name.push(ch)
}
},
Cursor::Arg => {
frag.args.push(ch)
},
_ => unreachable!(),
}
frag.whole_string.push(ch);
}
fn lex_branch_end_frag(&mut self, ch: char, frag: &mut MacroFragment, remainder: &mut String, lexor : &mut Lexor, level: usize, caller: &str) -> Result<(), RadError> {
frag.whole_string.push(ch);
if frag.name == "define" {
self.add_rule(frag, remainder)?;
lexor.escape_nl = true;
#[cfg(feature = "debug")]
{
if level == 0 { self.user_input_on_macro(&frag)?; }
else {self.user_input_on_step(&frag)?;}
if level == 0 {
self.line_caches.clear();
}
}
frag.clear();
}
else { #[cfg(feature = "debug")]
{
self.break_point(frag)?;
if frag.name.len() == 0 {
lexor.escape_nl = true;
return Ok(());
}
}
let evaluation_result = self.evaluate(level, caller, &frag.name, &frag.args, frag.greedy || self.always_greedy);
match evaluation_result {
Err(error) => {
self.lex_branch_end_frag_eval_result_error(error)?;
}
Ok(eval_variant) => {
self.lex_branch_end_frag_eval_result_ok(eval_variant,frag,remainder,lexor,level)?;
}
}
frag.clear()
}
Ok(())
}
fn lex_branch_end_frag_eval_result_error(&mut self, error : RadError) -> Result<(), RadError> {
if let RadError::Panic = error{
();
} else {
self.log_error(&format!("{}", error))?;
}
return Err(RadError::Panic);
}
fn lex_branch_end_frag_eval_result_ok(&mut self, variant : EvalResult, frag: &mut MacroFragment, remainder: &mut String, lexor : &mut Lexor, level: usize) -> Result<(), RadError> {
match variant {
EvalResult::Eval(content) => {
#[cfg(feature = "debug")]
if !self.is_local(level + 1, &frag.name) {
if level == 0 {self.user_input_on_macro(&frag)?;}
else {self.user_input_on_step(&frag)?;}
if level == 0 {
self.line_caches.clear();
}
}
if let None = content {
lexor.escape_nl = true;
} else {
let mut content = content.unwrap();
if frag.trimmed {
content = Utils::trim(&content)?;
}
if frag.yield_literal {
content = format!("\\*{}*\\", content);
}
if frag.pipe {
self.pipe_value = content;
lexor.escape_nl = true;
} else {
remainder.push_str(&content);
}
}
}
EvalResult::None => {
if self.strict {
self.log_error(&format!("Failed to invoke a macro : \"{}\"", frag.name))?;
return Err(RadError::StrictPanic);
}
if !self.purge {
self.log_error(&format!("Failed to invoke a macro : \"{}\"", frag.name))?;
remainder.push_str(&frag.whole_string);
} else {
lexor.escape_nl = true;
}
}
}
Ok(())
}
fn lex_branch_exit_frag(&mut self,ch: char, frag: &mut MacroFragment, remainder: &mut String) {
frag.whole_string.push(ch);
remainder.push_str(&frag.whole_string);
frag.clear();
}
pub(crate) fn get_map(&mut self) -> &mut MacroMap {
&mut self.map
}
pub(crate) fn get_auth_state(&self, auth_type : &AuthType) -> AuthState {
*self.auth_flags.get_state(auth_type)
}
pub(crate) fn set_temp_file(&mut self, path: &Path) {
self.temp_target = (path.to_owned(),OpenOptions::new()
.create(true)
.write(true)
.truncate(true)
.open(path)
.unwrap());
}
pub(crate) fn set_sandbox(&mut self) {
self.sandbox = true;
}
pub(crate) fn get_temp_path(&self) -> &Path {
self.temp_target.0.as_ref()
}
pub(crate) fn get_temp_file(&self) -> &File {
&self.temp_target.1
}
fn backup(&self) -> SandboxBackup {
SandboxBackup {
current_input: self.current_input.clone(),
local_macro_map: self.map.local.clone(),
logger_lines: self.logger.backup_lines(),
}
}
fn recover(&mut self, backup: SandboxBackup) {
self.logger.set_file(&backup.current_input);
self.current_input = backup.current_input;
self.map.local= backup.local_macro_map;
self.logger.recover_lines(backup.logger_lines);
}
pub(crate) fn log_error(&mut self, log : &str) -> Result<(), RadError> {
self.logger.elog(log)?;
Ok(())
}
pub(crate) fn log_warning(&mut self, log : &str) -> Result<(), RadError> {
self.logger.wlog(log)?;
Ok(())
}
fn set_file(&mut self, file: &str) -> Result<(), RadError> {
let path = Path::new(file);
if !path.exists() {
Err(RadError::InvalidCommandOption(format!("File, \"{}\" doesn't exist, therefore cannot be read by r4d.", path.display())))
} else {
self.current_input = file.to_owned();
self.logger.set_file(file);
Ok(())
}
}
fn set_input(&mut self, input: &str) -> Result<(), RadError> {
self.current_input = input.to_owned();
self.logger.set_file(input);
Ok(())
}
}
struct DefineParser{
arg_cursor :DefineCursor,
name: String,
args: String,
body: String,
bind: bool,
container: String,
}
impl DefineParser {
fn new() -> Self {
Self {
arg_cursor : DefineCursor::Name,
name : String::new(),
args : String::new(),
body : String::new(),
bind : false,
container : String::new(),
}
}
fn clear(&mut self) {
self.arg_cursor = DefineCursor::Name;
self.name.clear();
self.args.clear();
self.body.clear();
self.bind = false;
self.container.clear();
}
fn parse_define(&mut self, text: &str) -> Option<(String, String, String)> {
self.clear(); let mut char_iter = text.chars().peekable();
while let Some(ch) = char_iter.next() {
match self.arg_cursor {
DefineCursor::Name => {
if let ParseIgnore::Ignore = self.branch_name(ch) {continue;}
if !self.is_valid_name(ch) { return None; }
}
DefineCursor::Args => {
if let ParseIgnore::Ignore = self.branch_args(ch) {continue;}
if !self.is_valid_name(ch) { return None; }
}
DefineCursor::Body => ()
}
self.container.push(ch);
}
if self.args.len() == 0 && !self.bind {
return None;
}
self.body.push_str(&self.container);
Some((self.name.clone(), self.args.clone(), self.body.clone()))
}
fn is_valid_name(&mut self, ch : char) -> bool {
if self.container.len() == 0 { if !ch.is_alphabetic() {
return false;
}
} else { if !ch.is_alphanumeric() && ch != '_' {
return false;
}
}
true
}
fn branch_name(&mut self, ch: char) -> ParseIgnore {
if ch == '=' {
self.name.push_str(&self.container);
self.container.clear();
self.arg_cursor = DefineCursor::Body;
self.bind = true;
ParseIgnore::Ignore
}
else if Utils::is_blank_char(ch) {
if self.name.len() != 0 {
self.container.clear();
ParseIgnore::None
} else {
ParseIgnore::Ignore
}
}
else if ch == ',' {
self.name.push_str(&self.container);
self.container.clear();
self.arg_cursor = DefineCursor::Args;
ParseIgnore::Ignore
} else {
ParseIgnore::None
}
}
fn branch_args(&mut self, ch: char) -> ParseIgnore {
if Utils::is_blank_char(ch) && self.name.len() != 0 {
if self.container.len() != 0 {
self.args.push_str(&self.container);
self.args.push(' ');
self.container.clear();
}
ParseIgnore::Ignore
}
else if ch == '=' {
self.args.push_str(&self.container);
self.container.clear();
self.arg_cursor = DefineCursor::Body;
ParseIgnore::Ignore
}
else {
ParseIgnore::None
}
}
}
enum DefineCursor {
Name,
Args,
Body,
}
enum ParseIgnore {
Ignore,
None
}
#[derive(Debug)]
struct MacroFragment {
pub whole_string: String,
pub name: String,
pub args: String,
pub pipe: bool,
pub greedy: bool,
pub yield_literal : bool,
pub trimmed : bool,
}
impl MacroFragment {
fn new() -> Self {
MacroFragment {
whole_string : String::new(),
name : String::new(),
args : String::new(),
pipe: false,
greedy: false,
yield_literal : false,
trimmed: false,
}
}
fn clear(&mut self) {
self.whole_string.clear();
self.name.clear();
self.args.clear();
self.pipe = false;
self.greedy = false;
self.yield_literal = false;
self.trimmed = false;
}
fn is_empty(&self) -> bool {
self.whole_string.len() == 0
}
}
#[derive(Debug)]
enum ParseResult {
FoundMacro(String),
Printable(String),
NoPrint,
EOI,
}
struct SandboxBackup {
current_input: String,
local_macro_map: HashMap<String,String>,
logger_lines: LoggerLines,
}
pub(crate) mod auth {
#[derive(Debug)]
pub(crate) struct AuthFlags{
auths: Vec<AuthState>,
}
impl AuthFlags {
pub fn new() -> Self {
let mut auths = Vec::new();
for _ in 0..AuthType::LEN as usize {
auths.push(AuthState::Restricted);
}
Self {
auths,
}
}
pub fn set_state(&mut self, auth_type: &AuthType, auth_state: AuthState) {
self.auths[*auth_type as usize] = auth_state;
}
pub fn get_state(&self, auth_type: &AuthType) -> &AuthState {
&self.auths[*auth_type as usize]
}
}
#[derive(Debug, Clone, Copy)]
pub enum AuthType {
ENV = 0,
FIN = 1,
FOUT = 2,
CMD = 3,
LEN = 4,
}
impl AuthType {
pub fn from(string: &str) -> Option<Self> {
match string.to_lowercase().as_str() {
"fin" => Some(Self::FIN),
"fout" => Some(Self::FOUT),
"cmd" => Some(Self::CMD),
"env" => Some(Self::ENV),
_ => None
}
}
}
#[derive(Clone, Copy, Debug)]
pub(crate) enum AuthState {
Restricted,
Warn,
Open,
}
}
enum EvalResult {
Eval(Option<String>),
None,
}