- Шифрованный файл .qulyp: Argon2id + XChaCha20-Poly1305, атомарная запись и .bak - Записи с TOTP, папками, избранным и историей паролей; генератор паролей - Синхронизация через облачную папку с послезаписным слиянием и ручное слияние файлов - Автоблокировка, автоочистка буфера обмена, тёмная/светлая тема - Сборка под Windows и Linux, CI для Gitea Actions - Заметки проекта в notes/ с лимитами строк (tests/notes.rs) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
42 lines
1.6 KiB
Rust
42 lines
1.6 KiB
Rust
//! Заметки проекта не должны разрастаться: каждый файл в `notes/` объявляет
|
|
//! лимит строк в первой строке (`<!-- limit: N -->`), тест следит за ним.
|
|
|
|
use std::fs;
|
|
use std::path::Path;
|
|
|
|
#[test]
|
|
fn notes_fit_their_line_limits() {
|
|
let dir = Path::new(env!("CARGO_MANIFEST_DIR")).join("notes");
|
|
let mut problems = Vec::new();
|
|
let mut checked = 0;
|
|
|
|
for entry in fs::read_dir(&dir).expect("папка notes/") {
|
|
let path = entry.unwrap().path();
|
|
if path.extension().is_none_or(|e| e != "md") {
|
|
continue;
|
|
}
|
|
let name = path.file_name().unwrap().to_string_lossy().into_owned();
|
|
let text = fs::read_to_string(&path).unwrap();
|
|
let first = text.lines().next().unwrap_or_default();
|
|
let limit = first
|
|
.trim()
|
|
.strip_prefix("<!-- limit:")
|
|
.and_then(|s| s.strip_suffix("-->"))
|
|
.and_then(|s| s.trim().parse::<usize>().ok());
|
|
let Some(limit) = limit else {
|
|
problems.push(format!("{name}: нет заголовка `<!-- limit: N -->`"));
|
|
continue;
|
|
};
|
|
let lines = text.lines().count();
|
|
if lines > limit {
|
|
problems.push(format!(
|
|
"{name}: {lines} строк при лимите {limit} — сократите (LOG.md: перенесите старое в notes/archive/)"
|
|
));
|
|
}
|
|
checked += 1;
|
|
}
|
|
|
|
assert!(checked > 0, "в notes/ нет ни одного .md");
|
|
assert!(problems.is_empty(), "\n{}", problems.join("\n"));
|
|
}
|