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
115
116
117
118
#[cfg(test)]
mod tests;
mod fence;
mod rustdoc_parse;
use std::{
env, fs,
iter::zip,
path::{Path, PathBuf},
};
use anyhow::{bail, Result};
use fence::find_fences;
pub fn process_includes_document(document: &mut String, template_dir: &Path) -> Result<()> {
let mut fences = find_fences(&document, template_dir)?;
fences.sort_by_key(|f| f.priority());
for fence in fences {
fence.run(document)?;
}
Ok(())
}
pub fn update(template_file: &str, destination_file: &str) -> Result<()> {
let is_ci = env::var("CI").map(|_| true).unwrap_or(false);
let template_path = PathBuf::from(template_file);
let template_dir = template_path
.parent()
.map(|p| p.to_path_buf())
.unwrap_or_else(|| PathBuf::from(""));
let mut generated_doc = fs::read_to_string(&template_path)?;
process_includes_document(&mut generated_doc, &template_dir)?;
let generated_doc = format!(
r#"<!--
Please don't edit. This document has been generated from {template_file}
-->
{generated_doc}"#
);
let dest_path = PathBuf::from(destination_file);
let current_doc = if dest_path.exists() {
fs::read_to_string(&dest_path)?
} else {
"".to_string()
};
if let Some(diff_str) = diff(&generated_doc, ¤t_doc) {
if is_ci {
bail!(
"The markdown document {dest_path:?} is out of sync with {template_path:?}.
Please re-run the tests and commit the updated file.
This message is generated because the test is run on CI (the CI environment variable is set).\n{diff_str}"
);
} else {
fs::write(&dest_path, generated_doc.as_bytes())?;
}
}
Ok(())
}
fn diff(doc1: &str, doc2: &str) -> Option<String> {
if zip(doc1.lines(), doc2.lines()).any(|(l1, l2)| l1.trim() != l2.trim()) {
Some(
zip(doc1.lines(), doc2.lines())
.map(|(l1, l2)| format!("> {}\n< {}\n", l1.trim(), l2.trim()))
.collect::<Vec<_>>()
.join(", "),
)
} else {
None
}
}
#[test]
fn update_readme() {
update("src/README.tpl.md", "README.md").unwrap();
}