obsidian_parser/obfile/
obfile_on_disk.rs1use crate::error::Error;
2use crate::obfile::{ObFile, 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 raw_text = std::fs::read_to_string(&self.path).unwrap();
58 let (valid_properties, parts) = parse_obfile(&raw_text);
59
60 match (valid_properties, &parts[..]) {
61 (false, _) => raw_text,
62 (true, [_, _, content]) => (*content).trim().to_string(),
63 _ => unimplemented!(),
64 }
65 }
66
67 fn properties(&self) -> T {
72 let raw_text = std::fs::read_to_string(&self.path).unwrap();
73 let (valid_properties, parts) = parse_obfile(&raw_text);
74
75 match (valid_properties, &parts[..]) {
76 (false, _) => T::default(),
77 (true, [_, properties, _]) => serde_yml::from_str(properties).unwrap(),
78 _ => unreachable!(),
79 }
80 }
81
82 fn path(&self) -> Option<PathBuf> {
83 Some(self.path.clone())
84 }
85
86 fn from_string<P: AsRef<std::path::Path>>(
90 _raw_text: &str,
91 path: Option<P>,
92 ) -> Result<Self, Error> {
93 let path_buf = path.expect("Path is required").as_ref().to_path_buf();
94
95 Self::from_file(path_buf)
96 }
97
98 fn from_file<P: AsRef<std::path::Path>>(path: P) -> Result<Self, Error> {
100 let path_buf = path.as_ref().to_path_buf();
101
102 if !path_buf.is_file() {
103 return Err(Error::IsNotFile(path_buf));
104 }
105
106 Ok(Self {
107 path: path_buf,
108 phantom: PhantomData,
109 })
110 }
111}
112
113#[cfg(test)]
114mod tests {
115 use super::*;
116 use crate::obfile::ObFileDefault;
117 use crate::obfile::tests::{from_file, from_file_with_unicode, impl_test_for_obfile};
118 use crate::test_utils::init_test_logger;
119 use std::io::Write;
120 use tempfile::NamedTempFile;
121
122 impl_test_for_obfile!(impl_from_file, from_file, ObFileOnDisk);
123
124 impl_test_for_obfile!(
125 impl_from_file_with_unicode,
126 from_file_with_unicode,
127 ObFileOnDisk
128 );
129
130 #[test]
131 #[should_panic]
132 fn use_from_string_without_path() {
133 init_test_logger();
134 ObFileOnDisk::from_string_default("", None::<&str>).unwrap();
135 }
136
137 #[test]
138 #[should_panic]
139 fn use_from_file_with_path_not_file() {
140 init_test_logger();
141 let temp_dir = tempfile::tempdir().unwrap();
142
143 ObFileOnDisk::from_file_default(temp_dir.path()).unwrap();
144 }
145
146 #[test]
147 fn get_path() {
148 init_test_logger();
149 let test_file = NamedTempFile::new().unwrap();
150 let file = ObFileOnDisk::from_file_default(test_file.path()).unwrap();
151
152 assert_eq!(file.path().unwrap(), test_file.path());
153 assert_eq!(file.path, test_file.path());
154 }
155
156 #[test]
157 fn get_content() {
158 init_test_logger();
159 let test_data = "DATA";
160 let mut test_file = NamedTempFile::new().unwrap();
161 test_file.write_all(test_data.as_bytes()).unwrap();
162
163 let file = ObFileOnDisk::from_file_default(test_file.path()).unwrap();
164 assert_eq!(file.content(), test_data);
165 }
166
167 #[test]
168 fn get_properties() {
169 init_test_logger();
170 let test_data = "---\ntime: now\n---\nDATA";
171 let mut test_file = NamedTempFile::new().unwrap();
172 test_file.write_all(test_data.as_bytes()).unwrap();
173
174 let file = ObFileOnDisk::from_file_default(test_file.path()).unwrap();
175 assert_eq!(file.content(), "DATA");
176 assert_eq!(file.properties()["time"], "now");
177 }
178}