nmd_core/resource/
disk_resource.rs

1use std::{path::PathBuf, str::FromStr, fs::{self, OpenOptions}, io::Write};
2
3
4use super::{ResourceError, Resource};
5
6
7
8/// Resource which uses filesystem to store information 
9#[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()) {           // TODO: remove clone
121            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}