codegenr_lib/processor/
mod.rs1use std::collections::HashMap;
2
3mod clean;
4mod console;
5mod file;
6
7use clean::*;
8use console::*;
9use file::*;
10use glob::PatternError;
11use thiserror::Error;
12
13static INSTRUCTION_LINE_REGEX: once_cell::sync::Lazy<regex::Regex> =
14 once_cell::sync::Lazy::new(|| regex::Regex::new("^###.*$").expect("The INSTRUCTION_LINE_REGEX regex did not compile."));
15
16#[derive(Error, Debug)]
17pub enum ProcessorError {
18 #[error("Io Error: `{0}`.")]
19 Io(#[from] std::io::Error),
20 #[error("Pattern Error: {0}.")]
21 Pattern(#[from] PatternError),
22 #[error("Instruction name not found on line {0}: `{1}`.")]
23 InstructionNotFound(usize, String),
24 #[error("Instruction `{0}` doest not exist. Line {1}: `{2}`.")]
25 InstructionNotExisting(String, usize, String),
26 #[error("Closing tag found for `{0}` instruction while it does not need it. Line {1}: `{2}`.")]
27 ClosingTagFound(String, usize, String),
28 #[error("Missing openning tag for `{0}` instruction. Line {1}: `{2}`.")]
29 MissingOpeningTag(String, usize, String),
30 #[error("`{0}` instruction needs one '<{}>' parameter.")]
31 InstructionParameterMissing(&'static str, &'static str),
32 #[error("Error converting PathBuf to str.")]
33 PathBufToStrConvert,
34}
35
36pub trait Instruction {
37 fn command_name(&self) -> &'static str;
38 fn start(&self, params: Vec<String>) -> Result<Box<dyn InstructionLineHandler>, ProcessorError>;
39 fn needs_closing(&self) -> bool {
40 false
41 }
42}
43
44pub trait InstructionLineHandler {
45 fn handle_line(&self, line: &str) -> Result<(), ProcessorError>;
46}
47
48pub struct TranscientLineHandler;
49
50impl InstructionLineHandler for TranscientLineHandler {
51 fn handle_line(&self, _line: &str) -> Result<(), ProcessorError> {
52 Ok(())
53 }
54}
55
56fn get_instructions(output: String) -> HashMap<&'static str, Box<dyn Instruction>> {
57 let mut hash: HashMap<&'static str, Box<dyn Instruction>> = HashMap::<_, _>::with_capacity(3);
58 hash.insert(CLEAN, Box::new(CleanInstruction::new(output.clone())) as Box<dyn Instruction>);
59 hash.insert(FILE, Box::new(FileInstruction::new(output)) as Box<dyn Instruction>);
60 hash.insert(CONSOLE, Box::new(ConsoleInstruction) as Box<dyn Instruction>);
61 hash
62}
63
64#[::tracing::instrument(level = "trace")]
65pub fn process(content: &str, output: String) -> Result<(), ProcessorError> {
66 let instructions = get_instructions(output);
67 let mut active_handlers = HashMap::<String, Box<dyn InstructionLineHandler>>::new();
68
69 for (line_number, line) in content.lines().enumerate() {
70 let captures = INSTRUCTION_LINE_REGEX.find(line);
71 match captures {
72 None => {
73 for (_, h) in active_handlers.iter() {
74 h.handle_line(line)?;
75 }
76 }
77 Some(_match) => {
78 let net_line = line.trim_start_matches('#').trim_start();
79 let is_closing = net_line.starts_with('/');
80 let net_line = net_line.trim_start_matches('/').trim_start();
81
82 let mut words = net_line.split(' ').map(|s| s.trim()).filter(|s| !s.is_empty());
83 let instruction_name = words
84 .next()
85 .ok_or_else(|| ProcessorError::InstructionNotFound(line_number, line.into()))?
86 .to_uppercase();
87
88 let instruction = instructions
89 .get(&instruction_name.as_ref())
90 .ok_or_else(|| ProcessorError::InstructionNotExisting(instruction_name.clone(), line_number, line.into()))?;
91
92 match (is_closing, instruction.needs_closing()) {
93 (true, false) => {
94 return Err(ProcessorError::ClosingTagFound(instruction_name, line_number, line.into()));
95 }
96 (true, true) => {
97 active_handlers
98 .remove(&instruction_name)
99 .ok_or_else(|| ProcessorError::MissingOpeningTag(instruction_name, line_number, line.into()))?;
100 }
101 (false, _) => {
102 let handler = instruction.start(words.map(Into::into).collect())?;
103 if instruction.needs_closing() {
104 active_handlers.insert(instruction_name, handler);
105 }
106 }
107 }
108 }
109 }
110 }
111
112 Ok(())
113}
114
115#[cfg(test)]
116mod test {
117 use super::*;
118
119 #[test]
120 #[ignore]
121 fn process_test() -> Result<(), anyhow::Error> {
122 process(
123 r#"
124### FILE plop.rs
125test
126### /FILE
127### CONSOLE
128Hello
129###/ console
130### FILE plop2.rs
131test2
132### / FILE
133 "#,
134 ".".into(),
135 )?;
136
137 Ok(())
138 }
139}