KeiSeiKit-1.0/_primitives/_rust/kei-import-project/src/doc_walker.rs
Parfii-bot 0be354a920 KeiSeiKit-public — clean state
Single-commit clean baseline after security scrub of niche-tells,
project codenames, internal jargon, and contributor-email leaks.

Contents:
- 100 Rust crates (_primitives/_rust/)
- 37 agent manifests (_manifests/) + generated specs (_generated/)
- 67 user-invocable skills (skills/)
- 33 hooks (hooks/)
- Composition blocks (_blocks/)
- Documentation (docs/, README.md)
- TS adapter packages (_ts_packages/)
- Assembler (_assembler/)
- Roles (_roles/)
- Templates (_templates/)
- Forgejo CI (.forgejo/)

Author: Denis Parfionovich <info@greendragon.info>

License: see LICENSE.
2026-05-01 12:09:03 +08:00

100 lines
3.1 KiB
Rust

//! Walk a repo root and collect documentation markdown file paths.
//!
//! Collects: README.md / README / readme.md at top level + every
//! docs/**/*.md (skipping _*.md, .git/, target/, node_modules/).
use std::path::{Path, PathBuf};
use walkdir::WalkDir;
/// Return ordered list of candidate markdown paths to extract skills from.
pub fn collect_doc_paths(repo_root: &Path) -> Vec<PathBuf> {
let mut out = Vec::new();
collect_readmes(repo_root, &mut out);
collect_docs_dir(repo_root, &mut out);
out
}
fn collect_readmes(root: &Path, out: &mut Vec<PathBuf>) {
for name in &["README.md", "README", "readme.md"] {
let candidate = root.join(name);
if candidate.is_file() {
out.push(candidate);
break; // take first match only
}
}
}
fn collect_docs_dir(root: &Path, out: &mut Vec<PathBuf>) {
let docs_root = root.join("docs");
if !docs_root.is_dir() {
return;
}
for entry in WalkDir::new(&docs_root)
.follow_links(false)
.into_iter()
.flatten()
{
if should_skip(entry.path()) {
continue;
}
if entry.file_type().is_file() && is_markdown(entry.path()) {
out.push(entry.path().to_path_buf());
}
}
}
fn should_skip(path: &Path) -> bool {
path.components().any(|c| {
let s = c.as_os_str().to_string_lossy();
matches!(s.as_ref(), ".git" | "target" | "node_modules")
}) || path
.file_name()
.and_then(|n| n.to_str())
.map(|n| n.starts_with('_'))
.unwrap_or(false)
}
fn is_markdown(path: &Path) -> bool {
path.extension()
.and_then(|e| e.to_str())
.map(|e| e.eq_ignore_ascii_case("md"))
.unwrap_or(false)
}
#[cfg(test)]
mod tests {
use super::*;
use std::fs;
use tempfile::TempDir;
#[test]
fn finds_readme_and_docs() {
let dir = TempDir::new().unwrap();
fs::write(dir.path().join("README.md"), "# Hello").unwrap();
let docs = dir.path().join("docs");
fs::create_dir(&docs).unwrap();
fs::write(docs.join("guide.md"), "## Guide\nbody").unwrap();
fs::write(docs.join("_internal.md"), "skip").unwrap();
let paths = collect_doc_paths(dir.path());
assert_eq!(paths.len(), 2);
assert!(paths.iter().any(|p| p.ends_with("README.md")));
assert!(paths.iter().any(|p| p.ends_with("guide.md")));
assert!(!paths.iter().any(|p| p.ends_with("_internal.md")));
}
#[test]
fn skips_target_dir() {
let dir = TempDir::new().unwrap();
let docs = dir.path().join("docs");
fs::create_dir(&docs).unwrap();
let target_docs = docs.join("target");
fs::create_dir(&target_docs).unwrap();
fs::write(target_docs.join("build.md"), "skip me").unwrap();
fs::write(docs.join("real.md"), "## Real\nbody").unwrap();
let paths = collect_doc_paths(dir.path());
assert!(paths.iter().any(|p| p.ends_with("real.md")));
assert!(!paths.iter().any(|p| p.to_string_lossy().contains("target")));
}
}