libimagstore/file_abstraction/
fs.rs1use std::fs::{File, OpenOptions, create_dir_all, remove_file, copy, rename};
21use std::io::{Seek, SeekFrom, Read};
22use std::path::{Path, PathBuf};
23use std::sync::Arc;
24
25use libimagerror::errors::ErrorMsg as EM;
26
27use super::FileAbstraction;
28use super::FileAbstractionInstance;
29use super::Drain;
30use crate::store::Entry;
31use crate::storeid::StoreIdWithBase;
32use crate::file_abstraction::iter::PathIterator;
33use crate::file_abstraction::iter::PathIterBuilder;
34
35use walkdir::WalkDir;
36use failure::ResultExt;
37use failure::Fallible as Result;
38use failure::Error;
39
40#[derive(Debug)]
41pub struct FSFileAbstractionInstance(PathBuf);
42
43impl FileAbstractionInstance for FSFileAbstractionInstance {
44
45 fn get_file_content<'a>(&mut self, id: StoreIdWithBase<'a>) -> Result<Option<Entry>> {
49 debug!("Getting lazy file: {:?}", self);
50
51 let mut file = match open_file(&self.0) {
52 Err(err) => return Err(Error::from(err)),
53 Ok(None) => return Ok(None),
54 Ok(Some(file)) => file,
55 };
56
57 file.seek(SeekFrom::Start(0)).context(EM::FileNotSeeked)?;
58
59 let mut s = String::new();
60
61 file.read_to_string(&mut s)
62 .context(EM::IO)
63 .map_err(Error::from)
64 .map(|_| s)
65 .and_then(|s: String| Entry::from_str(id, &s))
66 .map(Some)
67 }
68
69 fn write_file_content(&mut self, buf: &Entry) -> Result<()> {
73 use std::io::Write;
74
75 let buf = buf.to_str()?.into_bytes();
76 let mut file = create_file(&self.0).context(EM::FileNotCreated)?;
77
78 file.seek(SeekFrom::Start(0)).context(EM::FileNotCreated)?;
79 file.set_len(buf.len() as u64).context(EM::FileNotWritten)?;
80 file.write_all(&buf)
81 .context(EM::FileNotWritten)
82 .map_err(Error::from)
83 }
84}
85
86#[derive(Debug, Default)]
90pub struct FSFileAbstraction {}
91
92impl FileAbstraction for FSFileAbstraction {
93
94 fn remove_file(&self, path: &PathBuf) -> Result<()> {
95 remove_file(path)
96 .context(EM::FileNotRemoved)
97 .map_err(Error::from)
98 }
99
100 fn copy(&self, from: &PathBuf, to: &PathBuf) -> Result<()> {
101 copy(from, to)
102 .map(|_| ())
103 .context(EM::FileNotCopied)
104 .map_err(Error::from)
105 }
106
107 fn rename(&self, from: &PathBuf, to: &PathBuf) -> Result<()> {
108 if let Some(p) = to.parent() {
109 if !p.exists() {
110 debug!("Creating: {:?}", p);
111 create_dir_all(&p).context(EM::DirNotCreated)?;
112 }
113 } else {
114 debug!("Failed to find parent. This looks like it will fail now");
115 }
117
118 debug!("Renaming {:?} to {:?}", from, to);
119 rename(from, to)
120 .context(EM::FileNotRenamed)
121 .map_err(Error::from)
122 }
123
124 fn create_dir_all(&self, path: &PathBuf) -> Result<()> {
125 debug!("Creating: {:?}", path);
126 create_dir_all(path)
127 .context(EM::DirNotCreated)
128 .map_err(Error::from)
129 }
130
131 fn exists(&self, path: &PathBuf) -> Result<bool> {
132 Ok(path.exists())
133 }
134
135 fn is_file(&self, path: &PathBuf) -> Result<bool> {
136 Ok(path.is_file())
137 }
138
139 fn new_instance(&self, p: PathBuf) -> Box<dyn FileAbstractionInstance> {
140 Box::new(FSFileAbstractionInstance(p))
141 }
142
143 fn drain(&self) -> Result<Drain> {
145 Ok(Drain::empty())
146 }
147
148 fn fill(&mut self, mut d: Drain) -> Result<()> {
151 d.iter().fold(Ok(()), |acc, (path, element)| {
152 acc.and_then(|_| self.new_instance(path).write_file_content(&element))
153 })
154 }
155
156 fn pathes_recursively<'a>(&self,
157 basepath: PathBuf,
158 storepath: &'a PathBuf,
159 backend: Arc<dyn FileAbstraction>)
160 -> Result<PathIterator<'a>>
161 {
162 trace!("Building PathIterator object");
163 Ok(PathIterator::new(Box::new(WalkDirPathIterBuilder { basepath }), storepath, backend))
164 }
165}
166
167#[derive(Debug)]
168pub struct WalkDirPathIterBuilder {
169 basepath: PathBuf
170}
171
172impl PathIterBuilder for WalkDirPathIterBuilder {
173 fn build_iter(&self) -> Box<dyn Iterator<Item = Result<PathBuf>>> {
174 trace!("Building iterator for {}", self.basepath.display());
175 Box::new(WalkDir::new(self.basepath.clone())
176 .min_depth(1)
177 .max_open(100)
178 .into_iter()
179 .filter(|r| match r {
180 Err(_) => true,
181 Ok(path) => path.file_type().is_file(),
182 })
183 .map(|r| {
184 trace!("Working in PathIterator with {:?}", r);
185 r.map(|e| PathBuf::from(e.path()))
186 .context(format_err!("Error in Walkdir"))
187 .map_err(Error::from)
188 }))
189 }
190
191 fn in_collection(&mut self, c: &str) -> Result<()> {
192 debug!("Altering PathIterBuilder path with: {:?}", c);
193 self.basepath.push(c);
194 debug!(" -> path : {:?}", self.basepath);
195
196 if !self.basepath.exists() {
197 Err(format_err!("Does not exist: {}", self.basepath.display()))
198 } else {
199 Ok(())
200 }
201 }
202}
203
204fn open_file<A: AsRef<Path>>(p: A) -> ::std::io::Result<Option<File>> {
205 match OpenOptions::new().write(true).read(true).open(p) {
206 Err(e) => match e.kind() {
207 ::std::io::ErrorKind::NotFound => Ok(None),
208 _ => Err(e),
209 },
210 Ok(file) => Ok(Some(file))
211 }
212}
213
214fn create_file<A: AsRef<Path>>(p: A) -> ::std::io::Result<File> {
215 if let Some(parent) = p.as_ref().parent() {
216 trace!("'{}' is directory = {}", parent.display(), parent.is_dir());
217 if !parent.is_dir() {
218 trace!("Implicitely creating directory: {:?}", parent);
219 create_dir_all(parent)?;
220 }
221 }
222 OpenOptions::new().write(true).read(true).create(true).open(p)
223}
224