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
89
90
91
92
93
94
95
96
97
98
99
100
use super::State;
use serde_json::Value;
use std::path::Path;
pub trait BFn: Fn(&mut Value) -> String {
fn clone_boxed(&self) -> Box<dyn BFn>;
}
impl<T> BFn for T
where
T: 'static + Clone + Fn(&mut Value) -> String,
{
fn clone_boxed(&self) -> Box<dyn BFn> {
Box::new(self.clone())
}
}
impl Clone for Box<dyn BFn> {
fn clone(&self) -> Self {
self.as_ref().clone_boxed()
}
}
#[derive(Clone)]
pub enum Renderer {
LoadAndApplyTemplate(&'static str),
Pipeline(Vec<Renderer>),
Custom(Box<dyn BFn>),
None,
}
impl std::fmt::Debug for Renderer {
fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
use Renderer::*;
match self {
LoadAndApplyTemplate(ref t) => write!(fmt, "Renderer::LoadAndApplyTemplate({})", t),
Pipeline(ref list) => write!(fmt, "Renderer::Pipeline({:?})", list),
Custom(_) => write!(fmt, "Renderer::Custom(_)"),
None => write!(fmt, "Renderer::None"),
}
}
}
impl Renderer {
pub fn check_mtime(&self, state: &mut State, dest_path: &Path) -> bool {
match self {
Renderer::LoadAndApplyTemplate(ref path) => {
state.check_mtime(dest_path, &Path::new(path))
}
Renderer::Pipeline(ref list) => list
.iter()
.fold(false, |acc, el| acc || el.check_mtime(state, dest_path)),
Renderer::None | Renderer::Custom(_) => true,
}
}
pub fn render(&self, state: &mut State, context: &mut Value) -> String {
match self {
Renderer::LoadAndApplyTemplate(ref path) => state.templates_render(path, context),
Renderer::Pipeline(ref list) => {
let mut iter = list.iter().peekable();
while let Some(stage) = iter.next() {
let new_body = stage.render(state, context);
if iter.peek().is_none() {
return new_body;
} else if let Value::Object(ref mut map) = context {
map.insert("body".to_string(), Value::String(new_body));
}
}
String::new()
}
Renderer::Custom(ref c) => c(context),
Renderer::None => String::new(),
}
}
}