diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/macro_processor/error.rs | 23 | ||||
| -rw-r--r-- | src/macro_processor/macro_processor.rs | 129 | ||||
| -rw-r--r-- | src/macro_processor/main.rs | 14 | ||||
| -rw-r--r-- | src/macro_processor/mod.rs | 1 | ||||
| -rw-r--r-- | src/skaldpress/error.rs | 11 | ||||
| -rw-r--r-- | src/skaldpress/main.rs | 29 | 
6 files changed, 151 insertions, 56 deletions
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<dyn Error>), +} + +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<String, SMPError> {      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<String, SMPError> {      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<String, SMPError> {      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<String, SMPError> {      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<String, SMPError> {      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<String, SMPError> {      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<String, SMPError> {      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<String, SMPError> {      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<String, SMPError>, +    ),      /// 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<String, SMPError> {          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<String, SMPError> {          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<String> = 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<Box<dyn Error>>),      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<String, SkaldpressError> {      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<String, SkaldpressError> {      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<String, SkaldpressError> {      };      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<dyn std::error::Error>> { @@ -53,7 +64,7 @@ fn compile_file_and_write(source_file_path: &Path) -> Result<(), Box<dyn std::er          .join(              source_file_path                  .strip_prefix(CONTENT_DIR) -                .map_err(|e| SkaldpressError::PathOperationError(4, Some(Box::new(e))))?, +                .map_err(|e| SkaldpressError::PathOperationError(SP_GEN_DEST_STRIP_PREFIX_ERROR, Some(Box::new(e))))?,          )          .with_extension("html");  | 
