1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
use crate::*;
use std::sync::Arc;

/// Compiler function
///
/// compiler function takes [`Context`] as parameter,
/// and returns [`CompilerReturn`] which is boxed future.
pub trait CompileFunction: Fn(Context) -> CompilerReturn + Send + Sync {}
impl<F> CompileFunction for F where F: Fn(Context) -> CompilerReturn + Send + Sync {}

/// Generic compiler
/// You can create new compiler using this.
pub struct GenericCompiler {
    compile_method: Box<dyn CompileFunction>,
}
impl GenericCompiler {
    pub fn empty() -> Self {
        Self::from(|ctx| compile!(Ok(ctx)))
    }
    /// Create compiler from closure which implements [`CompileFunction`].
    ///
    /// # Example
    /// ```
    /// use polysite::{compiler::utils::GenericCompiler, *};
    /// GenericCompiler::from(|ctx| compile!({ Ok(ctx) }));
    /// ```
    pub fn from<F: CompileFunction + 'static>(f: F) -> Self {
        Self {
            compile_method: Box::new(f),
        }
    }
}
impl Compiler for GenericCompiler {
    fn compile(&self, ctx: Context) -> CompilerReturn {
        (self.compile_method)(ctx)
    }
}

/// Pipe compiler
/// Create large compiler from piping small ones
pub struct PipeCompiler {
    compilers: Vec<Arc<dyn Compiler>>,
}
impl PipeCompiler {
    pub fn new(compilers: Vec<Arc<dyn Compiler>>) -> Self {
        Self { compilers }
    }
}
impl Compiler for PipeCompiler {
    fn compile(&self, ctx: Context) -> CompilerReturn {
        let compilers = self.compilers.clone();
        compile!({
            let mut ctx = ctx;
            for c in compilers {
                ctx = c.compile(ctx).await?;
            }
            Ok(ctx)
        })
    }
}

/// [`pipe!`] macro may used to make large compiler from
/// piping multiple compilers
///
/// # Example
/// This example will read source as Markdown and write HTML to target.
///
/// ```
/// use polysite::{compiler::*, *};
/// pipe!(
///     path::SetExtension::new("html"),
///     file::FileReader::new(),
///     markdown::MarkdownRenderer::new(None),
///     file::FileWriter::new(),
/// );
/// ```
#[macro_export]
macro_rules! pipe {
    ($f:expr, $($n:expr),+ $(,)?) => {{
        $crate::compiler::utils::PipeCompiler::new(vec![
            $f.get(),
            $(
                $n.get(),
            )+
        ])
    }}
}
pub use pipe;