From e6ab276a4d5daaf0dd40eed5e2ff477b48187f77 Mon Sep 17 00:00:00 2001 From: Qrius Date: Thu, 26 Sep 2024 00:11:05 +0200 Subject: Change return of MacroProcessor to Result --- src/macro_processor/error.rs | 23 ++++++ src/macro_processor/macro_processor.rs | 129 ++++++++++++++++++++++----------- src/macro_processor/main.rs | 14 +++- src/macro_processor/mod.rs | 1 + src/skaldpress/error.rs | 11 +++ src/skaldpress/main.rs | 29 +++++--- 6 files changed, 151 insertions(+), 56 deletions(-) create mode 100644 src/macro_processor/error.rs diff --git a/src/macro_processor/error.rs b/src/macro_processor/error.rs new file mode 100644 index 0000000..074a8ef --- /dev/null +++ b/src/macro_processor/error.rs @@ -0,0 +1,23 @@ +use std::error::Error; +use std::fmt; + +#[derive(Debug)] +pub enum SMPError { + IncludeError(u8, std::io::Error, String), + ShellCommandError(u8, Box), +} + +impl fmt::Display for SMPError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + SMPError::IncludeError(code, _, file) => { + write!(f, "[SMP{}] Error reading file \"{}\"", code, file) + } + SMPError::ShellCommandError(code, e) => { + write!(f, "[SMP{}] Error running shell command \"{:#?}\"", code, e) + } + } + } +} + +impl std::error::Error for SMPError {} diff --git a/src/macro_processor/macro_processor.rs b/src/macro_processor/macro_processor.rs index 0f8b217..f26780e 100644 --- a/src/macro_processor/macro_processor.rs +++ b/src/macro_processor/macro_processor.rs @@ -1,3 +1,4 @@ +use crate::macro_processor::error::SMPError; use std::collections::HashMap; use std::fs; use std::process::Command; @@ -39,24 +40,32 @@ macro_rules! highlight_debug { } /// Builtin for defining a new macro -fn smp_builtin_define(smp: &mut MacroProcessor, macro_name: &str, args: &mut [String]) -> String { +fn smp_builtin_define( + smp: &mut MacroProcessor, + macro_name: &str, + args: &mut [String], +) -> Result { if args.len() < 1 { - return macro_name.to_string(); + return Ok(macro_name.to_string()); } - let arg0 = smp.process_input(&args[0]); + let arg0 = smp.process_input(&args[0])?; if args.len() > 1 { - let arg1 = smp.process_input(&args[1]); + let arg1 = smp.process_input(&args[1])?; smp.define_macro(arg0, arg1); } else { smp.define_macro(arg0, String::new()); } - String::new() + Ok(String::new()) } /// If macro is defined, return second argument, else return third argument if provided -fn smp_builtin_ifdef(smp: &mut MacroProcessor, macro_name: &str, args: &mut [String]) -> String { +fn smp_builtin_ifdef( + smp: &mut MacroProcessor, + macro_name: &str, + args: &mut [String], +) -> Result { if args.len() < 2 { - return macro_name.to_string(); + return Ok(macro_name.to_string()); } // We need to expand the first argument here as well, but we need to make the parser // support literal, and phrase strings @@ -66,13 +75,17 @@ fn smp_builtin_ifdef(smp: &mut MacroProcessor, macro_name: &str, args: &mut [Str if args.len() > 2 { return smp.process_input(&args[2]); } - String::new() + Ok(String::new()) } /// If macro is not defined, return second argument, else return third argument if provided -fn smp_builtin_ifndef(smp: &mut MacroProcessor, macro_name: &str, args: &mut [String]) -> String { +fn smp_builtin_ifndef( + smp: &mut MacroProcessor, + macro_name: &str, + args: &mut [String], +) -> Result { if args.len() < 2 { - return macro_name.to_string(); + return Ok(macro_name.to_string()); } // We need to expand the first argument here as well, but we need to make the parser // support literal, and phrase strings @@ -82,85 +95,113 @@ fn smp_builtin_ifndef(smp: &mut MacroProcessor, macro_name: &str, args: &mut [St if args.len() > 2 { return smp.process_input(&args[2]); } - String::new() + Ok(String::new()) } /// If arguments are equal, return third argument, else return fourth argument if provided -fn smp_builtin_ifeq(smp: &mut MacroProcessor, macro_name: &str, args: &mut [String]) -> String { +fn smp_builtin_ifeq( + smp: &mut MacroProcessor, + macro_name: &str, + args: &mut [String], +) -> Result { if args.len() < 3 { - return macro_name.to_string(); + return Ok(macro_name.to_string()); } - let arg0 = smp.process_input(&args[0]); - let arg1 = smp.process_input(&args[1]); + let arg0 = smp.process_input(&args[0])?; + let arg1 = smp.process_input(&args[1])?; if arg0 == arg1 { return smp.process_input(&args[2]); } if args.len() > 3 { return smp.process_input(&args[3]); } - String::new() + Ok(String::new()) } /// If arguments are not equal, return third argument, else return fourth argument if provided -fn smp_builtin_ifneq(smp: &mut MacroProcessor, macro_name: &str, args: &mut [String]) -> String { +fn smp_builtin_ifneq( + smp: &mut MacroProcessor, + macro_name: &str, + args: &mut [String], +) -> Result { if args.len() < 3 { - return macro_name.to_string(); + return Ok(macro_name.to_string()); } - let arg0 = smp.process_input(&args[0]); - let arg1 = smp.process_input(&args[1]); + let arg0 = smp.process_input(&args[0])?; + let arg1 = smp.process_input(&args[1])?; if arg0 != arg1 { return smp.process_input(&args[2]); } if args.len() > 3 { return smp.process_input(&args[3]); } - return String::new(); + return Ok(String::new()); } /// Include a new file, and process it normally. There is no loop protection here! -fn smp_builtin_include(smp: &mut MacroProcessor, macro_name: &str, args: &mut [String]) -> String { +fn smp_builtin_include( + smp: &mut MacroProcessor, + macro_name: &str, + args: &mut [String], +) -> Result { if args.len() < 1 { - return macro_name.to_string(); + return Ok(macro_name.to_string()); } - let arg0 = smp.process_input(&args[0]); - let input_file = fs::read_to_string(&arg0).expect("Failed to read input file"); + let arg0 = smp.process_input(&args[0])?; + let input_file = fs::read_to_string(&arg0).map_err(|e| SMPError::IncludeError(2, e, arg0))?; return smp.process_input(&input_file); } /// Simply execute argument as shell command -fn smp_builtin_shell(smp: &mut MacroProcessor, macro_name: &str, args: &mut [String]) -> String { +fn smp_builtin_shell( + smp: &mut MacroProcessor, + macro_name: &str, + args: &mut [String], +) -> Result { if args.len() < 1 { - return macro_name.to_string(); + return Ok(macro_name.to_string()); } - let arg0 = smp.process_input(&args[0]); + let arg0 = smp.process_input(&args[0])?; let res = Command::new("sh").arg("-c").arg(arg0).output(); match res { - Ok(output) => String::from_utf8(output.stdout).expect("SMP1"), - Err(_) => String::new(), + Ok(output) => String::from_utf8(output.stdout) + .map_err(|e| SMPError::ShellCommandError(1, Box::new(e))), + Err(_) => Ok(String::new()), } } /// Would like one that is better than this tbh -fn smp_builtin_expr(smp: &mut MacroProcessor, macro_name: &str, args: &mut [String]) -> String { +fn smp_builtin_expr( + smp: &mut MacroProcessor, + macro_name: &str, + args: &mut [String], +) -> Result { if args.len() < 1 { - return macro_name.to_string(); + return Ok(macro_name.to_string()); } for arg in args.iter_mut() { - *arg = smp.process_input(&arg); + *arg = smp.process_input(&arg)?; } let res = Command::new("expr").args(args).output(); match res { - Ok(output) => String::from_utf8(output.stdout).expect("SMP1"), - Err(_) => String::new(), + Ok(output) => String::from_utf8(output.stdout) + .map_err(|e| SMPError::ShellCommandError(1, Box::new(e))), + Err(_) => Ok(String::new()), } } /// Types of macros, this is to make it easy to store both functions and strings pub enum MacroType { /// When expanded, the associated function will be expanded - Function(fn(smp: &mut MacroProcessor, macro_name: &str, args: &mut [String]) -> String), + Function( + fn( + smp: &mut MacroProcessor, + macro_name: &str, + args: &mut [String], + ) -> Result, + ), /// Will be expanded in-place to the String String(String), } @@ -247,9 +288,9 @@ impl MacroProcessor { /// /// * `macro_name` - Name of macro to expand if it exists /// * `args` - List of arguments parsed along with macro invokation (empty list if no arguments were parsed) - fn expand_macro(&mut self, macro_name: &str, args: &mut [String]) -> String { + fn expand_macro(&mut self, macro_name: &str, args: &mut [String]) -> Result { let Some(macro_body) = self.macros.get(macro_name) else { - return format!("{}", macro_name); + return Ok(format!("{}", macro_name)); }; match macro_body { @@ -262,7 +303,7 @@ impl MacroProcessor { let placeholder = format!("${}", i + 1); expanded = expanded.replace(&placeholder, arg); } - return expanded; + return Ok(expanded); } MacroType::Function(func) => { return func(self, macro_name, args); @@ -281,7 +322,7 @@ impl MacroProcessor { /// # Arguments /// /// * `input` - The text to process - pub fn process_input(&mut self, input: &str) -> String { + pub fn process_input(&mut self, input: &str) -> Result { let mut output = String::new(); let mut state = ParserState::Normal; let mut macro_name = String::new(); @@ -325,7 +366,7 @@ impl MacroProcessor { if macro_name == "SNNL" { skip_next_line_ending = c != '\n'; } else { - let expanded = self.expand_macro(¯o_name, &mut []); + let expanded = self.expand_macro(¯o_name, &mut [])?; output.push_str(&expanded); output.push(c); } @@ -338,7 +379,7 @@ impl MacroProcessor { highlight_debug!("\x1b[32m\x1b[7m", input, (macro_name_start -> i)); macro_args.push(argument.trim().to_string()); - let expanded = self.expand_macro(¯o_name, &mut macro_args); + let expanded = self.expand_macro(¯o_name, &mut macro_args)?; output.push_str(&expanded); state = ParserState::Normal; macro_name.clear(); @@ -356,9 +397,9 @@ impl MacroProcessor { // Handle cases where the text ends with a macro without arguments if !macro_name.is_empty() { - output.push_str(&self.expand_macro(¯o_name, &mut [])); + output.push_str(&self.expand_macro(¯o_name, &mut [])?); } - output + Ok(output) } } diff --git a/src/macro_processor/main.rs b/src/macro_processor/main.rs index 247fc85..2d34952 100644 --- a/src/macro_processor/main.rs +++ b/src/macro_processor/main.rs @@ -4,9 +4,17 @@ use std::fs; fn main() { let args: Vec = env::args().collect(); - let input_file = fs::read_to_string(&args[1]).expect("Failed to read input file"); + let input_file = match fs::read_to_string(&args[1]) { + Ok(x) => x, + Err(e) => { + println!("Could not read input-file \"{}\": {}", args[1], e); + std::process::exit(1); + } + }; let mut macro_processor = MacroProcessor::new(); - let final_output = macro_processor.process_input(&input_file); - println!("{}", final_output); + match macro_processor.process_input(&input_file) { + Ok(out) => println!("{}", out), + Err(e) => println!("Error {}", e), + } } diff --git a/src/macro_processor/mod.rs b/src/macro_processor/mod.rs index 2fccbda..ffc2144 100644 --- a/src/macro_processor/mod.rs +++ b/src/macro_processor/mod.rs @@ -1,2 +1,3 @@ +pub mod error; pub mod macro_processor; pub use macro_processor::MacroProcessor; diff --git a/src/skaldpress/error.rs b/src/skaldpress/error.rs index f8639b6..65a89ab 100644 --- a/src/skaldpress/error.rs +++ b/src/skaldpress/error.rs @@ -1,6 +1,13 @@ +use crate::macro_processor::error::SMPError; use std::error::Error; use std::fmt; +pub const SP_COMPILE_FILE_TEMPLATE_READ_ERROR: u8 = 2; +pub const SP_COMPILE_FILE_EXTENSION_ERROR_2: u8 = 3; +pub const SP_GEN_DEST_STRIP_PREFIX_ERROR: u8 = 4; +pub const SP_COMPILE_FILE_EXTENSION_ERROR: u8 = 7; +pub const SP_COMPILE_FILE_MACRO_PROCESS_ERROR: u8 = 9; + #[derive(Debug)] pub enum SkaldpressError { TemplateReadError(u8, std::io::Error, String), @@ -8,6 +15,7 @@ pub enum SkaldpressError { DirectoryReadError(u8, std::io::Error, String), PathOperationError(u8, Option>), DirectoryCreateError(u8, std::io::Error, String), + SMPError(u8, SMPError), } impl fmt::Display for SkaldpressError { @@ -28,6 +36,9 @@ impl fmt::Display for SkaldpressError { SkaldpressError::DirectoryCreateError(code, _, dir) => { write!(f, "[SP{}] Directory create error \"{}\"", code, dir) } + SkaldpressError::SMPError(code, e) => { + write!(f, "[SP{}] Macro processing error \"{:#?}\"", code, e) + } } } } diff --git a/src/skaldpress/main.rs b/src/skaldpress/main.rs index 79ea7ab..3563bf7 100644 --- a/src/skaldpress/main.rs +++ b/src/skaldpress/main.rs @@ -3,6 +3,11 @@ use std::path::Path; use skaldpress::macro_processor::MacroProcessor; use skaldpress::skaldpress::error::SkaldpressError; +use skaldpress::skaldpress::error::{ + SP_COMPILE_FILE_EXTENSION_ERROR, SP_COMPILE_FILE_EXTENSION_ERROR_2, + SP_COMPILE_FILE_MACRO_PROCESS_ERROR, SP_COMPILE_FILE_TEMPLATE_READ_ERROR, + SP_GEN_DEST_STRIP_PREFIX_ERROR, +}; use skaldpress::skaldpress::metadata_parser::extract_parse_yaml_metadata; const TEMPLATES_DIR: &str = "templates/"; @@ -12,7 +17,10 @@ const BUILD_DIR: &str = "build/"; fn compile_file(file_path: &Path) -> Result { let extension = file_path .extension() - .ok_or(SkaldpressError::PathOperationError(7, None))?; + .ok_or(SkaldpressError::PathOperationError( + SP_COMPILE_FILE_EXTENSION_ERROR, + None, + ))?; let file_content = fs::read_to_string(file_path).map_err(|e| { SkaldpressError::FileReadError( @@ -24,8 +32,10 @@ fn compile_file(file_path: &Path) -> Result { let (map, file_content) = extract_parse_yaml_metadata(&file_content); let file_content = match extension .to_str() - .ok_or(SkaldpressError::PathOperationError(3, None))? - { + .ok_or(SkaldpressError::PathOperationError( + SP_COMPILE_FILE_EXTENSION_ERROR_2, + None, + ))? { "md" => markdown::to_html(file_content), _ => file_content.to_string(), }; @@ -35,17 +45,18 @@ fn compile_file(file_path: &Path) -> Result { }; let template_file = format!("{}{}.html", TEMPLATES_DIR, template); - let template = fs::read_to_string(&template_file) - .map_err(|e| SkaldpressError::TemplateReadError(2, e, template_file))?; + let template = fs::read_to_string(&template_file).map_err(|e| { + SkaldpressError::TemplateReadError(SP_COMPILE_FILE_TEMPLATE_READ_ERROR, e, template_file) + })?; let mut macro_processor = MacroProcessor::new(); for (key, value) in map { macro_processor.define_macro(format!("METADATA_{}", key), value.to_string()); } macro_processor.define_macro(String::from("CONTENT"), file_content); - let final_output = macro_processor.process_input(&template); - - Ok(final_output) + macro_processor + .process_input(&template) + .map_err(|e| SkaldpressError::SMPError(SP_COMPILE_FILE_MACRO_PROCESS_ERROR, e)) } fn compile_file_and_write(source_file_path: &Path) -> Result<(), Box> { @@ -53,7 +64,7 @@ fn compile_file_and_write(source_file_path: &Path) -> Result<(), Box