136 lines
4.2 KiB
Rust
136 lines
4.2 KiB
Rust
// 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` or `src/common` folder or if we are in a `ffi.rs`
|
|
// file.
|
|
if p.file_name().unwrap() == OsStr::new("ffi.rs") {
|
|
return res;
|
|
}
|
|
let path = format!(
|
|
"/{}",
|
|
p.parent().unwrap().display().to_string().replace('\\', "/")
|
|
);
|
|
if path.ends_with("/src") || path.ends_with("src/common") {
|
|
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
|
|
}
|