vault_tasks_core/
vault_parser.rs

1use 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                    // recursive call for this subdir
63                    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}