Skip to main content

basalt_core/obsidian/
vault_entry.rs

1use std::{
2    fs,
3    path::{Path, PathBuf},
4};
5
6use super::{Error, Note, Result};
7
8#[derive(Debug, Clone, PartialEq)]
9#[allow(missing_docs)]
10pub enum VaultEntry {
11    File(Note),
12    Directory {
13        name: String,
14        path: PathBuf,
15        entries: Vec<VaultEntry>,
16    },
17}
18
19impl VaultEntry {
20    #[allow(missing_docs)]
21    pub fn name(&self) -> &str {
22        match self {
23            Self::Directory { name, .. } => name,
24            Self::File(note) => note.name(),
25        }
26    }
27}
28
29impl TryFrom<&Path> for VaultEntry {
30    type Error = Error;
31    fn try_from(value: &Path) -> Result<Self> {
32        let name = value
33            .with_extension("")
34            .file_name()
35            .map(|file_name| file_name.to_string_lossy().into_owned())
36            .ok_or_else(|| Error::EmptyFileName(value.to_path_buf()))?;
37
38        if value.is_file() {
39            let note = Note::try_from((name, value.to_path_buf()))?;
40            Ok(VaultEntry::File(note))
41        } else {
42            Ok(VaultEntry::Directory {
43                name,
44                path: value.to_path_buf(),
45                entries: fs::read_dir(value)
46                    .into_iter()
47                    .flatten()
48                    .filter_map(|entry| {
49                        // NOTE: Might want to propagate the try_into errors further up
50                        entry
51                            .map_err(Error::from)
52                            .and_then(|entry| entry.path().as_path().try_into())
53                            .ok()
54                    })
55                    .collect(),
56            })
57        }
58    }
59}
60
61#[allow(missing_docs)]
62pub trait FindNote {
63    #[allow(missing_docs)]
64    fn find_note<'a>(&'a self, path: &Path) -> Option<&'a Note>;
65}
66
67impl FindNote for Vec<VaultEntry> {
68    fn find_note<'a>(&'a self, path: &Path) -> Option<&'a Note> {
69        self.iter().find_map(|entry| entry.find_note(path))
70    }
71}
72
73impl FindNote for VaultEntry {
74    fn find_note<'a>(&'a self, path: &Path) -> Option<&'a Note> {
75        match self {
76            VaultEntry::File(note) if note.path() == path => Some(note),
77            VaultEntry::Directory {
78                entries,
79                path: dir_path,
80                ..
81            } if path.starts_with(dir_path) => entries.find_note(path),
82            _ => None,
83        }
84    }
85}