Skip to main content

obsidian_cli_inspector/
scanner.rs

1use 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            // Check if should exclude
45            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                // Only index markdown files
53                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}