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
129
|
// Take a look at the license at the top of the repository in the LICENSE file.
use super::utils::{show_error, TestResult};
use std::ffi::OsStr;
use std::path::Path;
fn to_correct_name(s: &str) -> String {
let mut out = String::with_capacity(s.len());
for c in s.chars() {
if c.is_uppercase() {
if !out.is_empty() {
out.push('_');
}
out.push_str(c.to_lowercase().to_string().as_str());
} else {
out.push(c);
}
}
out
}
fn check_md_doc_path(p: &Path, md_line: &str, ty_line: &str) -> bool {
let parts = md_line.split('/').collect::<Vec<_>>();
if let Some(md_name) = parts.last().and_then(|n| n.split(".md").next()) {
if let Some(name) = ty_line.split_whitespace().filter(|s| !s.is_empty()).nth(2) {
if let Some(name) = name
.split('<')
.next()
.and_then(|n| n.split('{').next())
.and_then(|n| n.split('(').next())
.and_then(|n| n.split(';').next())
{
let correct = to_correct_name(name);
if correct.as_str() == md_name {
return true;
}
show_error(
p,
&format!(
"Invalid markdown file name `{md_name}`, should have been `{correct}`",
),
);
return false;
}
}
show_error(p, &format!("Cannot extract type name from `{ty_line}`"));
} else {
show_error(p, &format!("Cannot extract md name from `{md_line}`"));
}
false
}
fn check_doc_comments_before(p: &Path, lines: &[&str], start: usize) -> bool {
let mut found_docs = false;
for pos in (0..start).rev() {
let trimmed = lines[pos].trim();
if trimmed.starts_with("///") {
if !lines[start].trim().starts_with("pub enum ThreadStatus {") {
show_error(
p,
&format!(
"Types should use common documentation by using `#[doc = include_str!(` \
and by putting the markdown file in the `md_doc` folder instead of `{}`",
&lines[pos],
),
);
return false;
}
return true;
} else if trimmed.starts_with("#[doc = include_str!(") {
found_docs = true;
if !check_md_doc_path(p, trimmed, lines[start]) {
return false;
}
} else if !trimmed.starts_with("#[") && !trimmed.starts_with("//") {
break;
}
}
if !found_docs {
show_error(
p,
&format!(
"Missing documentation for public item: `{}` (if it's not supposed to be a public \
item, use `pub(crate)` instead)",
lines[start],
),
);
return false;
}
true
}
pub fn check_docs(content: &str, p: &Path) -> TestResult {
let mut res = TestResult {
nb_tests: 0,
nb_errors: 0,
};
// No need to check if we are in the `src` folder or if we are in a `ffi.rs` file.
if p.parent().unwrap().file_name().unwrap() == OsStr::new("src")
|| p.file_name().unwrap() == OsStr::new("ffi.rs")
{
return res;
}
let lines = content.lines().collect::<Vec<_>>();
for pos in 1..lines.len() {
let line = lines[pos];
let trimmed = line.trim();
if trimmed.starts_with("//!") {
show_error(p, "There shouln't be inner doc comments (`//!`)");
res.nb_tests += 1;
res.nb_errors += 1;
continue;
} else if !line.starts_with("pub fn ")
&& !trimmed.starts_with("pub struct ")
&& !trimmed.starts_with("pub enum ")
{
continue;
}
res.nb_tests += 1;
if !check_doc_comments_before(p, &lines, pos) {
res.nb_errors += 1;
}
}
res
}
|