vault_tasks_core/
vault_parser.rs1use color_eyre::{eyre::bail, Result};
2use std::{
3 fs::{self, DirEntry},
4 path::Path,
5};
6use tracing::{debug, info};
7
8use crate::{parser::parser_file_entry::ParserFileEntry, TasksConfig};
9
10use super::vault_data::VaultData;
11
12pub struct VaultParser {
13 config: TasksConfig,
14}
15
16impl VaultParser {
17 pub const fn new(config: TasksConfig) -> Self {
18 Self { config }
19 }
20 pub fn scan_vault(&self) -> Result<VaultData> {
21 let mut tasks =
22 VaultData::Directory(self.config.vault_path.to_str().unwrap().to_owned(), vec![]);
23 info!("Scanning {:?}", self.config.vault_path);
24 self.scan(&self.config.vault_path, &mut tasks)?;
25 Ok(tasks)
26 }
27
28 fn scan(&self, path: &Path, tasks: &mut VaultData) -> Result<()> {
29 if self.config.ignored.contains(&path.to_owned()) {
30 debug!("Ignoring {path:?} (ignored list)");
31 return Ok(());
32 }
33
34 let entries = if path.is_dir() {
35 path.read_dir()?
36 .collect::<Vec<Result<DirEntry, std::io::Error>>>()
37 } else {
38 path.parent()
39 .unwrap()
40 .read_dir()?
41 .filter(|e| {
42 let e = e.as_ref().unwrap();
43 e.file_name().eq(&path.file_name().unwrap())
44 })
45 .collect::<Vec<Result<DirEntry, std::io::Error>>>()
46 };
47
48 for entry_err in entries {
49 let Ok(entry) = entry_err else { continue };
50 let name = entry.file_name().into_string().unwrap();
51 if !self.config.parse_dot_files && name.starts_with('.') {
52 debug!("Ignoring {name:?} (dot file)");
53 continue;
54 }
55 if self.config.ignored.contains(&entry.path()) {
56 debug!("Ignoring {name:?} (ignored list)");
57 continue;
58 }
59
60 if let VaultData::Directory(_, children) = tasks {
61 if entry.path().is_dir() {
62 let mut new_child = VaultData::Directory(
64 entry.file_name().to_str().unwrap().to_owned(),
65 vec![],
66 );
67
68 self.scan(&entry.path(), &mut new_child)?;
69
70 if let VaultData::Directory(_, c) = new_child.clone() {
71 if !c.is_empty() {
72 children.push(new_child);
73 }
74 }
75 } else if !std::path::Path::new(
76 &entry.file_name().into_string().unwrap_or_default(),
77 )
78 .extension()
79 .map_or(false, |ext| ext.eq_ignore_ascii_case("md"))
80 {
81 debug!("Ignoring {name:?} (not a .md file)");
82 continue;
83 } else if let Some(file_tasks) = self.parse_file(&entry) {
84 children.push(file_tasks);
85 }
86 } else {
87 bail!("Error while scanning directories, FileEntry was not a Directory");
88 }
89 }
90 Ok(())
91 }
92
93 fn parse_file(&self, entry: &DirEntry) -> Option<VaultData> {
94 debug!("Parsing {:?}", entry.file_name());
95 let content = fs::read_to_string(entry.path()).unwrap_or_default();
96 let mut parser = ParserFileEntry {
97 config: &self.config,
98 filename: String::new(),
99 };
100
101 parser.parse_file(entry.file_name().to_str().unwrap(), &content.as_str())
102 }
103}