use std::{fmt::Debug, io::Read, sync::Arc};
#[derive(Debug, Clone)]
pub enum PreprocessorError {
ParserError(String),
Other(String),
}
pub type PreprocessorResult<T> = Result<T, PreprocessorError>;
pub trait Preprocessor: Send + Sync {
fn preprocess(&self, code: &str) -> PreprocessorResult<String>;
}
impl<F> Preprocessor for F
where
F: Fn(&str) -> PreprocessorResult<String> + Send + Sync + Clone,
{
fn preprocess(&self, code: &str) -> PreprocessorResult<String> {
self(code)
}
}
#[derive(Clone)]
pub struct PreprocessorBundle {
pub(crate) preprocessors: Vec<Arc<dyn Preprocessor>>,
}
impl Default for PreprocessorBundle {
fn default() -> Self {
Self::new()
}
}
impl PreprocessorBundle {
pub fn new() -> Self {
Self {
preprocessors: Vec::new(),
}
}
pub fn add_preprocessor(mut self, preprocessor: impl Preprocessor + 'static) -> Self {
self.preprocessors.push(Arc::new(preprocessor));
self
}
pub fn preprocess(&self, code: &mut impl Read) -> String {
let mut code = std::io::read_to_string(code).unwrap();
for preprocessor in &self.preprocessors {
code = match preprocessor.preprocess(&code) {
Ok(code) => code,
Err(err) => panic!("Preprocessor error: {:?}", err),
};
}
code
}
}
#[cfg(test)]
mod tests {
#[test]
fn test_preprocessor_bundle() {
use super::*;
let bundle = PreprocessorBundle::new()
.add_preprocessor(|code: &str| Ok(code.replace("a", "b")))
.add_preprocessor(|code: &str| Ok(code.replace("b", "c")));
let code = "a";
let code = bundle.preprocess(&mut code.as_bytes());
assert_eq!(code, "c");
}
}