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
119
120
121
122
123
124
125
126
127
128
use crate::{depinfo::DependencyInfo, diagnostic::Diagnostic, input::InputFile, output};
use log::debug;
use memchr::{memchr2, memmem};
use std::{io, process::ExitCode};
pub enum Check {
UpToDate,
InvalidDepInfo(anyhow::Error),
InputChanged,
IncompatibleVersion(String),
OutdatedMarkdown,
OutputChanged
}
impl Check {
pub fn print<T: Into<String>>(&self, filename: T) -> io::Result<()> {
self.print_to(filename, io::stderr())
}
pub fn print_to<T, W>(&self, filename: T, out: W) -> Result<(), io::Error>
where
T: Into<String>,
W: io::Write
{
let mut diag = Diagnostic::new(filename.into(), String::new());
match self {
Check::UpToDate => {
diag.info("Readme is up to date");
},
Check::InvalidDepInfo(e) => {
diag.warn(format_args!("Readme has invalid dependency info: {e}"));
},
Check::InputChanged => diag.error("Input has changed"),
Check::IncompatibleVersion(name) => {
diag.error(format_args!(
"Readme links to incompatible version of dependency `{name}`"
));
},
Check::OutdatedMarkdown => {
diag.error(
"The readme was created with an outdated version of this tool"
);
},
Check::OutputChanged => {
diag.error("Readme has changed");
}
}
diag.print_to(out)
}
pub fn is_ok(&self) -> bool {
matches!(self, Self::UpToDate)
}
}
impl From<Check> for ExitCode {
fn from(check: Check) -> Self {
match check.is_ok() {
true => Self::SUCCESS,
false => Self::FAILURE
}
}
}
pub fn check_up2date(
input: InputFile,
template: &str,
check_file: &mut dyn io::Read
) -> anyhow::Result<Check> {
let mut check_buf = Vec::new();
check_file.read_to_end(&mut check_buf)?;
let search_key = b" [__cargo_doc2readme_dependencies_info]: ";
if let Some(search_idx) = memmem::find(&check_buf, search_key) {
let sub = &check_buf[search_idx + search_key.len() ..];
let end_idx = memchr2(b' ', b'\n', sub).unwrap_or(sub.len());
let depinfo_str = String::from_utf8(sub[.. end_idx].to_vec()).unwrap();
let depinfo = match DependencyInfo::decode(depinfo_str) {
Ok(depinfo) => depinfo,
Err(e) => {
return Ok(Check::InvalidDepInfo(e));
}
};
if depinfo.check_outdated() {
return Ok(Check::OutdatedMarkdown);
}
if !depinfo.check_input(template, &input.rustdoc) {
return Ok(Check::InputChanged);
}
for (lib_name, dep) in &input.dependencies {
debug!("Checking {} = \"{}\"", dep.crate_name, dep.req);
if !depinfo.check_dependency(&dep.crate_name, Some(&dep.req), lib_name, true)
{
return Ok(Check::IncompatibleVersion(dep.crate_name.clone()));
}
}
return Ok(Check::UpToDate);
}
let mut output_buf = Vec::new();
output::emit(input, template, &mut output_buf)?;
Ok(if output_buf == check_buf {
Check::UpToDate
} else {
Check::OutputChanged
})
}