Add initial project structure and core functionality for ArchDoc

- Created `.gitignore` files for various directories to exclude unnecessary files.
- Added `PLAN.md` to outline the project goals and architecture documentation generation.
- Implemented the `archdoc-cli` with a command-line interface for initializing and generating documentation.
- Developed the `archdoc-core` library for analyzing Python projects and generating architecture documentation.
- Included caching mechanisms to optimize repeated analysis.
- Established a comprehensive test suite to ensure functionality and error handling.
- Updated `README.md` to provide an overview and installation instructions for ArchDoc.
This commit is contained in:
2026-01-25 20:17:37 +03:00
commit 3701cee205
36 changed files with 7394 additions and 0 deletions

View File

@@ -0,0 +1,86 @@
//! File scanner for ArchDoc
//!
//! This module handles scanning the file system for Python files according to
//! the configuration settings.
use crate::config::Config;
use crate::errors::ArchDocError;
use std::path::{Path, PathBuf};
use walkdir::WalkDir;
pub struct FileScanner {
config: Config,
}
impl FileScanner {
pub fn new(config: Config) -> Self {
Self { config }
}
pub fn scan_python_files(&self, root: &Path) -> Result<Vec<PathBuf>, ArchDocError> {
// Check if root directory exists
if !root.exists() {
return Err(ArchDocError::Io(std::io::Error::new(
std::io::ErrorKind::NotFound,
format!("Root directory does not exist: {}", root.display())
)));
}
if !root.is_dir() {
return Err(ArchDocError::Io(std::io::Error::new(
std::io::ErrorKind::InvalidInput,
format!("Root path is not a directory: {}", root.display())
)));
}
let mut python_files = Vec::new();
// Walk directory tree respecting include/exclude patterns
for entry in WalkDir::new(root)
.follow_links(self.config.scan.follow_symlinks)
.into_iter() {
let entry = entry.map_err(|e| {
ArchDocError::Io(std::io::Error::new(
std::io::ErrorKind::Other,
format!("Failed to read directory entry: {}", e)
))
})?;
let path = entry.path();
// Skip excluded paths
if self.is_excluded(path) {
if path.is_dir() {
continue;
} else {
continue;
}
}
// Include Python files
if path.extension().and_then(|s| s.to_str()) == Some("py") {
python_files.push(path.to_path_buf());
}
}
Ok(python_files)
}
fn is_excluded(&self, path: &Path) -> bool {
// Convert path to string for pattern matching
let path_str = match path.to_str() {
Some(s) => s,
None => return false, // If we can't convert to string, don't exclude
};
// Check if path matches any exclude patterns
for pattern in &self.config.scan.exclude {
if path_str.contains(pattern) {
return true;
}
}
false
}
}