obsidian_parser/obfile/
obfile_on_disk.rs1use crate::error::Error;
2use crate::obfile::{ObFile, ResultParse, parse_obfile};
3use serde::de::DeserializeOwned;
4use std::marker::PhantomData;
5use std::{collections::HashMap, path::PathBuf};
6
7#[derive(Debug, Default, PartialEq, Clone)]
32pub struct ObFileOnDisk<T = HashMap<String, serde_yml::Value>>
33where
34    T: DeserializeOwned + Default + Clone + Send,
35{
36    pub path: PathBuf,
38
39    phantom: PhantomData<T>,
40}
41
42impl<T: DeserializeOwned + Default + Clone + Send> ObFile<T> for ObFileOnDisk<T> {
43    fn content(&self) -> String {
57        let data = std::fs::read(&self.path).unwrap();
58        let raw_text = String::from_utf8(data).unwrap();
59
60        match parse_obfile(&raw_text).unwrap() {
61            ResultParse::WithProperties {
62                content,
63                properties: _,
64            } => {
65                #[cfg(feature = "logging")]
66                log::trace!("Frontmatter detected, parsing properties");
67
68                content.to_string()
69            }
70            ResultParse::WithoutProperties => {
71                #[cfg(feature = "logging")]
72                log::trace!("No frontmatter found, storing raw content");
73
74                raw_text
75            }
76        }
77    }
78
79    fn properties(&self) -> T {
84        let data = std::fs::read(&self.path).unwrap();
85        let raw_text = String::from_utf8(data).unwrap();
86
87        match parse_obfile(&raw_text).unwrap() {
88            ResultParse::WithProperties {
89                content: _,
90                properties,
91            } => {
92                #[cfg(feature = "logging")]
93                log::trace!("Frontmatter detected, parsing properties");
94
95                serde_yml::from_str(properties).unwrap()
96            }
97            ResultParse::WithoutProperties => {
98                #[cfg(feature = "logging")]
99                log::trace!("No frontmatter found, storing raw content");
100
101                T::default()
102            }
103        }
104    }
105
106    fn path(&self) -> Option<PathBuf> {
107        Some(self.path.clone())
108    }
109
110    fn from_string<P: AsRef<std::path::Path>>(
114        _raw_text: &str,
115        path: Option<P>,
116    ) -> Result<Self, Error> {
117        let path_buf = path.expect("Path is required").as_ref().to_path_buf();
118
119        Self::from_file(path_buf)
120    }
121
122    fn from_file<P: AsRef<std::path::Path>>(path: P) -> Result<Self, Error> {
124        let path_buf = path.as_ref().to_path_buf();
125
126        if !path_buf.is_file() {
127            return Err(Error::IsNotFile(path_buf));
128        }
129
130        Ok(Self {
131            path: path_buf,
132            phantom: PhantomData,
133        })
134    }
135}
136
137#[cfg(test)]
138mod tests {
139    use super::*;
140    use crate::obfile::ObFileDefault;
141    use crate::obfile::impl_tests::{from_file, from_file_with_unicode, impl_test_for_obfile};
142    use crate::test_utils::init_test_logger;
143    use std::io::Write;
144    use tempfile::NamedTempFile;
145
146    impl_test_for_obfile!(impl_from_file, from_file, ObFileOnDisk);
147
148    impl_test_for_obfile!(
149        impl_from_file_with_unicode,
150        from_file_with_unicode,
151        ObFileOnDisk
152    );
153
154    #[test]
155    #[should_panic]
156    fn use_from_string_without_path() {
157        init_test_logger();
158        ObFileOnDisk::from_string_default("", None::<&str>).unwrap();
159    }
160
161    #[test]
162    #[should_panic]
163    fn use_from_file_with_path_not_file() {
164        init_test_logger();
165        let temp_dir = tempfile::tempdir().unwrap();
166
167        ObFileOnDisk::from_file_default(temp_dir.path()).unwrap();
168    }
169
170    #[test]
171    fn get_path() {
172        init_test_logger();
173        let test_file = NamedTempFile::new().unwrap();
174        let file = ObFileOnDisk::from_file_default(test_file.path()).unwrap();
175
176        assert_eq!(file.path().unwrap(), test_file.path());
177        assert_eq!(file.path, test_file.path());
178    }
179
180    #[test]
181    fn get_content() {
182        init_test_logger();
183        let test_data = "DATA";
184        let mut test_file = NamedTempFile::new().unwrap();
185        test_file.write_all(test_data.as_bytes()).unwrap();
186
187        let file = ObFileOnDisk::from_file_default(test_file.path()).unwrap();
188        assert_eq!(file.content(), test_data);
189    }
190
191    #[test]
192    fn get_properties() {
193        init_test_logger();
194        let test_data = "---\ntime: now\n---\nDATA";
195        let mut test_file = NamedTempFile::new().unwrap();
196        test_file.write_all(test_data.as_bytes()).unwrap();
197
198        let file = ObFileOnDisk::from_file_default(test_file.path()).unwrap();
199        assert_eq!(file.content(), "DATA");
200        assert_eq!(file.properties()["time"], "now");
201    }
202}