nmd_core/resource/
disk_resource.rs1use std::{path::PathBuf, str::FromStr, fs::{self, OpenOptions}, io::Write};
2
3
4use super::{ResourceError, Resource};
5
6
7
8#[derive(Debug, Clone)]
10pub struct DiskResource {
11 name: String,
12 location: PathBuf
13}
14
15impl DiskResource {
16 pub fn new(location: PathBuf) -> Result<Self, ResourceError> {
17
18 Self::try_from(location)
19 }
20
21 pub fn create_parents_dir(&self) -> Result<(), ResourceError> {
22
23 let prefix = self.location.parent().unwrap();
24
25 if !prefix.exists() {
26
27 std::fs::create_dir_all(prefix)?;
28 }
29
30 Ok(())
31 }
32}
33
34impl FromStr for DiskResource {
35 type Err = ResourceError;
36
37 fn from_str(s: &str) -> Result<Self, Self::Err> {
38
39 if s.is_empty() {
40 return Err(ResourceError::Creation("resource cannot be an empty string".to_string()));
41 }
42
43 Self::try_from(PathBuf::from_str(s).unwrap())
44 }
45}
46
47
48impl ToString for DiskResource {
49 fn to_string(&self) -> String {
50 self.location().to_string_lossy().to_string()
51 }
52}
53
54impl TryFrom<PathBuf> for DiskResource {
55 type Error = ResourceError;
56
57 fn try_from(location: PathBuf) -> Result<Self, Self::Error> {
58 if location.is_dir() {
59 return Err(ResourceError::InvalidResourceVerbose(format!("{} is a directory", location.to_string_lossy())))
60 }
61
62 if let Some(name) = location.file_name() {
63 Ok(Self {
64 name: name.to_string_lossy().to_string(),
65 location
66 })
67 } else {
68 Err(ResourceError::InvalidResource)
69 }
70 }
71}
72
73impl Resource for DiskResource {
74
75 type LocationType = PathBuf;
76
77 fn location(&self) -> &PathBuf {
78 &self.location
79 }
80
81 fn name(&self) -> &String {
82 &self.name
83 }
84
85 fn write(&mut self, content: &str) -> Result<(), ResourceError> {
86
87 let file_path = &self.location;
88
89 let mut file = OpenOptions::new()
90 .write(true)
91 .truncate(true)
92 .create(true)
93 .open(file_path)?;
94
95 file.write_all(content.as_bytes())?;
96
97 file.flush()?;
98 file.sync_all()?;
99
100 Ok(())
101 }
102
103 fn append(&mut self, content: &str) -> Result<(), ResourceError> {
104 let file_path = &self.location;
105
106 let mut file = OpenOptions::new()
107 .create(true)
108 .append(true)
109 .open(file_path)?;
110
111 file.write_all(content.as_bytes())?;
112
113 file.flush()?;
114 file.sync_all()?;
115
116 Ok(())
117 }
118
119 fn read(&self) -> Result<String, ResourceError> {
120 match fs::read_to_string(self.location.clone()) { Ok(content) => Ok(content),
122 Err(err) => Err(ResourceError::ReadError(format!("error during read content of {}: {}", self.to_string(), err.to_string())))
123 }
124 }
125
126 fn erase(&mut self) -> Result<(), ResourceError> {
127 if self.location.exists() {
128 return Ok(fs::remove_file(self.location.clone())?)
129 }
130
131 Ok(())
132 }
133}
134
135
136#[cfg(test)]
137mod test {
138
139 use super::*;
140
141 #[test]
142 #[should_panic]
143 fn dir() {
144 let project_directory = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
145 let dossier_dir = "nmd-test-dossier-1";
146 let nmd_file = project_directory.join("test-resources").join(dossier_dir);
147
148 let _ = DiskResource::new(nmd_file).unwrap();
149 }
150
151 #[test]
152 fn write() {
153 let project_directory = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
154 let dossier_dir = "nmd-test-dossier-1";
155 let nmd_file = project_directory.join("test-resources").join(dossier_dir).join("document-to-write.nmd");
156
157 let nmd_text =
158r#"
159#1 title 1
160## title 2
161###### title 6
162"#.trim();
163
164 let mut resource = DiskResource::try_from(nmd_file).unwrap();
165
166 resource.write(nmd_text).unwrap();
167
168 assert_eq!(nmd_text, resource.content().unwrap());
169
170 resource.erase().unwrap();
171 }
172}