structure-check: gate counts code lines, docs and comments in English
Gate ignores blank and full-line comment lines so the 250-line limit measures code bulk, not documentation weight; docs/README.md synced. Translate code comments to English in classfile, core config, namegen, rename assign, constants forms. Expand Plausible word bank with client-ecosystem terms (Module, Setting, Mixin, ...). cargo fmt clean, check.sh green.
This commit is contained in:
parent
ec28ec82e3
commit
39178420e4
42 changed files with 532 additions and 437 deletions
|
|
@ -1,7 +1,8 @@
|
|||
//! Compile-time gate for the project structure rules.
|
||||
//!
|
||||
//! Every crate calls [`check`] from its `build.rs`, so a file that grows past
|
||||
//! 250 lines or a directory that fills up with a fifth file fails the build.
|
||||
//! 250 *code* lines (comments and blank lines excluded) or a directory that
|
||||
//! fills up with a fifth file fails the build.
|
||||
|
||||
mod rules;
|
||||
|
||||
|
|
@ -48,6 +49,26 @@ mod tests {
|
|||
assert!(found[0].contains("lines >"), "{found:?}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ignores_comments_and_blank_lines() {
|
||||
// A file padded with comment and blank lines stays under the limit:
|
||||
// only real code counts toward `MAX_FILE_LINES`.
|
||||
let tmp = TempDir::new("comments");
|
||||
let mut body = String::new();
|
||||
for _ in 0..super::MAX_FILE_LINES * 2 {
|
||||
body.push_str("// a comment line\n");
|
||||
body.push_str("/// a doc line\n");
|
||||
body.push_str("//! a module line\n");
|
||||
body.push('\n');
|
||||
}
|
||||
for index in 0..40 {
|
||||
body.push_str(&format!("pub fn f{index}() {{}}\n"));
|
||||
}
|
||||
fs::write(tmp.0.join("lib.rs"), body).expect("write");
|
||||
let found = violations(&tmp.0);
|
||||
assert!(found.is_empty(), "{found:?}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn rejects_directory_over_file_limit() {
|
||||
let tmp = TempDir::new("many");
|
||||
|
|
@ -79,8 +100,8 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn ignores_markdown_size_and_count() {
|
||||
// Правила — только про Rust-код: `.md` не лимитирован ни по
|
||||
// строкам, ни в счётчике файлов директории.
|
||||
// Rules cover Rust code only: `.md` is bounded neither by the line
|
||||
// limit nor by the per-directory file count.
|
||||
let tmp = TempDir::new("md");
|
||||
for index in 0..super::MAX_DIR_FILES + 3 {
|
||||
fs::write(tmp.0.join(format!("doc{index}.md")), "line\n".repeat(300)).expect("write");
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
//! Project structure rules, invoked from every crate's `build.rs`.
|
||||
//!
|
||||
//! Rules apply to Rust code only, not to documentation:
|
||||
//! * every `.rs` file is at most [`MAX_FILE_LINES`] lines long;
|
||||
//! * every `.rs` file holds at most [`MAX_FILE_LINES`] *code* lines — blank
|
||||
//! lines and full-line comments (`//`, `///`, `//!`) are not counted;
|
||||
//! * every directory inside a crate's `src/`, `tests/` or `benches/` holds
|
||||
//! at most [`MAX_DIR_FILES`] files — subdirectories are unlimited.
|
||||
//!
|
||||
|
|
@ -13,7 +14,10 @@ use std::ffi::OsStr;
|
|||
use std::fs;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
/// Maximum number of lines in a Rust source file.
|
||||
/// Maximum number of code lines in a Rust source file.
|
||||
///
|
||||
/// Comment and blank lines are excluded (see [`count_code_lines`]) so the
|
||||
/// gate measures real code bulk, not documentation weight.
|
||||
pub const MAX_FILE_LINES: usize = 250;
|
||||
|
||||
/// Maximum number of files per directory; subdirectories are not counted.
|
||||
|
|
@ -35,9 +39,9 @@ pub fn check() {
|
|||
let manifest = manifest_dir();
|
||||
let mut violations = Vec::new();
|
||||
|
||||
// `src/` обязателен; `tests/` и `benches/` проверяются, если созданы —
|
||||
// иначе правило «250 строк» обходится выносом кода в тесты. `docs/` вне
|
||||
// гейта: правила — только про Rust-код.
|
||||
// `src/` is required; `tests/` and `benches/` are checked only when they
|
||||
// exist — otherwise the line rule is bypassed by moving code into tests.
|
||||
// `docs/` stays out of the gate: the rules are about Rust code only.
|
||||
let roots = [
|
||||
manifest.join("src"),
|
||||
manifest.join("tests"),
|
||||
|
|
@ -124,25 +128,34 @@ pub fn scan(root: &Path, violations: &mut Vec<String>) {
|
|||
}
|
||||
|
||||
fn check_line_limit(path: &Path, violations: &mut Vec<String>) {
|
||||
let lines = count_lines(path);
|
||||
let lines = count_code_lines(path);
|
||||
if lines > MAX_FILE_LINES {
|
||||
violations.push(format!(
|
||||
"{}: {lines} lines > {MAX_FILE_LINES}",
|
||||
"{}: {lines} code lines > {MAX_FILE_LINES}",
|
||||
path.display()
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
/// Counts lines without requiring the file to be valid UTF-8.
|
||||
fn count_lines(path: &Path) -> usize {
|
||||
let Ok(bytes) = fs::read(path) else {
|
||||
return 0;
|
||||
/// Counts *code* lines, skipping blanks and full-line comments.
|
||||
///
|
||||
/// A line is code when, after leading whitespace is stripped, it is non-empty
|
||||
/// and does not start with `//` (which also covers `///` and `//!`). A line
|
||||
/// that carries code followed by a trailing comment still counts once. Falls
|
||||
/// back to a raw newline count when the file is not valid UTF-8.
|
||||
fn count_code_lines(path: &Path) -> usize {
|
||||
let Ok(text) = fs::read_to_string(path) else {
|
||||
let Ok(bytes) = fs::read(path) else {
|
||||
return 0;
|
||||
};
|
||||
return bytes.iter().filter(|&&byte| byte == b'\n').count();
|
||||
};
|
||||
if bytes.is_empty() {
|
||||
return 0;
|
||||
}
|
||||
let newlines = bytes.iter().filter(|&&b| b == b'\n').count();
|
||||
newlines + usize::from(bytes.last() != Some(&b'\n'))
|
||||
text.lines()
|
||||
.filter(|line| {
|
||||
let trimmed = line.trim_start();
|
||||
!trimmed.is_empty() && !trimmed.starts_with("//")
|
||||
})
|
||||
.count()
|
||||
}
|
||||
|
||||
fn manifest_dir() -> PathBuf {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue