basalt_core/obsidian/
vault_entry.rs

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