obsidian_cli_inspector/
scanner.rs1use anyhow::Result;
2use std::fs;
3use std::path::{Path, PathBuf};
4
5#[derive(Debug, Clone)]
6pub struct FileEntry {
7 pub path: PathBuf,
8 pub relative_path: String,
9 pub mtime: u64,
10 pub size: u64,
11}
12
13pub struct VaultScanner {
14 vault_path: PathBuf,
15 exclude_patterns: Vec<String>,
16}
17
18impl VaultScanner {
19 pub fn new(vault_path: PathBuf, exclude_patterns: Vec<String>) -> Self {
20 VaultScanner {
21 vault_path,
22 exclude_patterns,
23 }
24 }
25
26 pub fn scan(&self) -> Result<Vec<FileEntry>> {
27 let mut entries = Vec::new();
28 self.walk_dir(&self.vault_path, &mut entries)?;
29 Ok(entries)
30 }
31
32 fn walk_dir(&self, dir: &Path, entries: &mut Vec<FileEntry>) -> Result<()> {
33 let mut dir_entries = Vec::new();
34 for entry in fs::read_dir(dir)? {
35 dir_entries.push(entry?);
36 }
37
38 dir_entries.sort_by_key(|a| a.path());
39
40 for entry in dir_entries {
41 let path = entry.path();
42 let relative_path = path.strip_prefix(&self.vault_path)?.to_path_buf();
43
44 if self.should_exclude(&relative_path) {
46 continue;
47 }
48
49 if path.is_dir() {
50 self.walk_dir(&path, entries)?;
51 } else if path.is_file() {
52 if let Some(ext) = path.extension() {
54 if ext == "md" {
55 let metadata = fs::metadata(&path)?;
56 let mtime = metadata
57 .modified()?
58 .duration_since(std::time::UNIX_EPOCH)?
59 .as_secs();
60
61 entries.push(FileEntry {
62 path,
63 relative_path: relative_path.to_string_lossy().to_string(),
64 mtime,
65 size: metadata.len(),
66 });
67 }
68 }
69 }
70 }
71 Ok(())
72 }
73
74 fn should_exclude(&self, path: &Path) -> bool {
75 let path_str = path.to_string_lossy();
76 for pattern in &self.exclude_patterns {
77 if path_str.contains(pattern) {
78 return true;
79 }
80 }
81 false
82 }
83}