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
101
102
103
104
105
106
107
108
109
110
111
112
113
114
use std::fmt;
use regex::Regex;
use handlebars::{Handlebars, Helper, RenderContext, RenderError};
use serde_json::to_string_pretty;
use super::{ChangeLog, OutputPreferences, PostProcessor, Result};
type RenderResult = ::std::result::Result<(), RenderError>;
pub fn render(clog: &ChangeLog, out: &OutputPreferences) -> Result<String> {
let text = if out.json {
to_string_pretty(clog).map_err(|e| format_err!("JSON render failed: {}", e))
} else {
let mut hbs = Handlebars::new();
hbs.register_helper("tidy-change", Box::new(tidy));
hbs.template_render(&out.get_template()?, clog)
.map_err(|e| format_err!("Handlebar render failed: {}", e))
};
text.map(|s| post_process(&s, &out.post_processors))
}
fn tidy(h: &Helper, _: &Handlebars, rc: &mut RenderContext) -> RenderResult {
if let Some(indent) = h.param(0).and_then(|v| v.value().as_str()) {
if let Some(text) = h.param(1).and_then(|v| v.value().as_str()) {
let mut lines = text.lines();
if let Some(first) = lines.next() {
writeln!(rc.writer, "{}", first.trim())?;
}
for line in lines {
writeln!(rc.writer, "{}{}", indent, line)?;
}
}
}
Ok(())
}
impl fmt::Display for ChangeLog {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let out = OutputPreferences::default();
match render(self, &out) {
Ok(fine) => write!(f, "{}", fine),
Err(err) => write!(f, "Error: {}", err),
}
}
}
fn post_process(output: &str, post_processors: &[PostProcessor]) -> String {
let mut processors = Vec::new();
for processor in post_processors {
if let Ok(lookup) = Regex::new(&processor.lookup) {
info!("Using post-processor {:#?}", lookup);
processors.push((lookup, processor.replace.as_str()));
} else {
warn!("Post-processor {:#?} is invalid", processor);
}
}
let mut processed = Vec::new();
for line in output.lines() {
let mut next: String = line.to_string();
for processor in &processors {
next = processor.0.replace_all(&next, processor.1).to_string();
}
processed.push(next);
}
processed.join("\n")
}
#[cfg(test)]
mod tests {
use super::PostProcessor;
#[test]
fn post_process() {
let input = String::from("Fixed JIRA-1234\nfoo");
let mut jira = PostProcessor::default();
jira.lookup = r"JIRA-(?P<t>\d+)".to_string();
jira.replace = r"[JIRA-$t](https://our.jira/$t)".to_string();
let out = super::post_process(&input, &vec![jira]);
assert_eq!(&out, "Fixed [JIRA-1234](https://our.jira/1234)\nfoo");
let mut bad = PostProcessor::default();
bad.lookup = r"JIRA-?(P<t\d+".to_string();
bad.replace = r"whatever".to_string();
let out = super::post_process(&input, &vec![bad]);
assert_eq!(&out, &input);
}
}