Skip to main content

fs_pro/
dir.rs

1use crate::error;
2use crate::file;
3use crate::file::File;
4use crate::path_stuff;
5use fs_extra;
6use std::collections::{HashMap, HashSet};
7use std::ffi::OsString;
8use std::fs;
9use std::path::{Path, PathBuf};
10
11/// the Dir struct is a struct for helping you
12/// working with directories
13#[derive(Debug, Clone)]
14pub struct Dir {
15  /// the path of directory
16  pub path: PathBuf,
17}
18
19/// the result of Dir.read()
20/// this struct express a item in a folder
21#[derive(Debug)]
22#[allow(missing_docs)]
23pub enum DirEntry {
24  File(File),
25  Dir(Dir),
26}
27
28impl DirEntry {
29  /// get the path of entry
30  pub fn path(&self) -> PathBuf {
31    match self {
32      DirEntry::File(entry) => entry.path.clone(),
33      DirEntry::Dir(entry) => entry.path.clone(),
34    }
35  }
36  /// get the raw name of entry
37  pub fn file_name(&self) -> OsString {
38    match self {
39      DirEntry::File(entry) => entry.path.file_name().unwrap().to_os_string(),
40      DirEntry::Dir(entry) => entry.path.file_name().unwrap().to_os_string(),
41    }
42  }
43  /// return true if entry is a file
44  #[allow(dead_code)]
45  pub fn is_file(&self) -> bool {
46    if let DirEntry::File(_) = self {
47      true
48    } else {
49      false
50    }
51  }
52  /// returns true if entry is a folder
53  #[allow(dead_code)]
54  pub fn is_dir(&self) -> bool {
55    if let DirEntry::Dir(_) = self {
56      true
57    } else {
58      false
59    }
60  }
61}
62
63impl AsRef<Path> for Dir {
64  fn as_ref(&self) -> &Path {
65    self.path.as_path()
66  }
67}
68
69// new and prop-like methods
70#[allow(dead_code)]
71impl Dir {
72  /// creates a new Dir
73  /// ```
74  /// use fs_pro::Dir;
75  ///
76  /// let dir = Dir::new("/path/to/dir").unwrap();
77  /// let dir = Dir::new(Path::new("/path/to/dir")).unwrap();
78  /// let dir = Dir::new(PathBuf::from("/path/to/dir"));
79  /// ```
80  /// # Errors
81  /// - given path is file
82  /// - invalid path
83  pub fn new<'a, P: 'a + AsRef<Path>>(path: P) -> error::Result<Dir> {
84    let mut path_buf = PathBuf::new();
85    path_buf.push(path);
86    let real_path = path_buf.as_path();
87    if real_path.exists() && real_path.is_file() {
88      Err(error::Error::new_from_kind(error::ErrorKind::InvalidFolder))
89    } else {
90      Ok(Dir { path: path_buf })
91    }
92  }
93  /// creates a directory in the temp directory
94  /// ```
95  /// use fs_pro::Dir;
96  ///
97  /// let temp_dir = Dir::temp_dir("name").unwrap();
98  /// ```
99  pub fn temp_dir<'a, P: 'a + AsRef<Path>>(name: P) -> error::Result<Dir> {
100    let dir = Dir::temp_dir_no_create(name)?;
101    dir.create()?;
102    Ok(dir)
103  }
104  /// like `temp_dir` but doesn't create the directory
105  /// ```
106  /// use fs_pro::Dir;
107  ///
108  /// let temp_dir = Dir::temp_dir_no_create("name").unwrap();
109  /// ```
110  pub fn temp_dir_no_create<'a, P: 'a + AsRef<Path>>(name: P) -> error::Result<Dir> {
111    let mut tmp_dir = std::env::temp_dir();
112    tmp_dir.push(name);
113    let dir = Dir::new(tmp_dir)?;
114    Ok(dir)
115  }
116  /// create a directory in the temp directory with random name
117  /// ```
118  /// use fs_pro::Dir;
119  ///
120  /// let temp_dir = Dir::temp_dir_rand();
121  /// ```
122  pub fn temp_dir_rand() -> error::Result<Dir> {
123    Ok(Dir::temp_dir(path_stuff::get_rand_chars(10))?)
124  }
125  /// like `temp_dir_rand` but doesn't create the directory
126  /// ```
127  /// use fs_pro::Dir;
128  ///
129  /// let temp_dir = Dir::temp_dir_rand_no_create();
130  /// ```
131  pub fn temp_dir_rand_no_create() -> error::Result<Dir> {
132    Ok(Dir::temp_dir_no_create(path_stuff::get_rand_chars(10))?)
133  }
134  /// gets the parent of the directory in &str
135  /// ```
136  /// use fs_pro::Dir;
137  ///
138  /// let dir = Dir::temp_dir_rand().unwrap();
139  /// assert_eq!(dir.parent().unwrap(), "/tmp");
140  /// ```
141  pub fn parent(&self) -> error::Result<&str> {
142    path_stuff::parent(self.path.as_path())
143  }
144  /// gets the name of the directory in &str
145  /// ```
146  /// use fs_pro::Dir;
147  ///
148  /// let dir = Dir::temp_dir("my_dir").unwrap();
149  /// assert_eq!(dir.name().unwrap(), "my_dir");
150  /// ```
151  pub fn name(&self) -> error::Result<&str> {
152    path_stuff::name(self.path.as_path())
153  }
154  /// parses the path and returns fs_pro::ParedPathDir
155  pub fn parse_path(&self) -> error::Result<path_stuff::ParsedPathDir<'_>> {
156    path_stuff::parse_path_dir(self.path.as_path())
157  }
158  /// get the size of directory in bytes
159  pub fn size(&self) -> error::Result<u64> {
160    Ok(error::result_from_fse(fs_extra::dir::get_size(&self.path))?)
161  }
162}
163
164#[allow(dead_code)]
165impl Dir {
166  /// return true if file exists
167  pub fn exists(&self) -> bool {
168    self.path.exists()
169  }
170  /// creates the directory if doesn't already exits
171  pub fn create(&self) -> error::Result<()> {
172    if self.path.exists() {
173      Ok(())
174    } else {
175      error::result_from_io(fs::create_dir(&self.path))
176    }
177  }
178  /// creates the directory and it's parent if doesn't exists
179  pub fn create_all(&self) -> error::Result<()> {
180    error::result_from_io(fs::create_dir_all(&self.path))?;
181    Ok(())
182  }
183  /// delete the directory even if empty
184  pub fn delete(&self) -> error::Result<()> {
185    error::result_from_fse(fs_extra::dir::remove(&self.path))
186  }
187  /// create a file inside the directory and return fs_pro::File
188  /// ```
189  /// let file = dir.create_file("hi.txt")?;
190  /// file.write("some thing")?;
191  /// // ...
192  /// ```
193  pub fn create_file<P: AsRef<Path>>(&self, name: P) -> error::Result<file::File> {
194    let mut file_path = PathBuf::new();
195    file_path.push(&self.path);
196    file_path.push(name);
197    let file = file::File::new(file_path)?;
198    file.create()?;
199    Ok(file)
200  }
201  /// create a directory inside the directory and returns fs_pro::Dir
202  /// ```
203  /// let sub_dir = dir.create_dir("sub_dir")?;
204  /// sub_dir.create_file("hello.txt")?;
205  /// // ...
206  /// ```
207  pub fn create_dir<P: AsRef<Path>>(&self, name: P) -> error::Result<Dir> {
208    let mut file_path = PathBuf::new();
209    file_path.push(&self.path);
210    file_path.push(name);
211    let dir = Dir::new(file_path)?;
212    dir.create()?;
213    Ok(dir)
214  }
215  /// delete a file inside the directory
216  /// ```
217  /// dir.delete_file("to_delete.txt")?;
218  /// ```
219  pub fn delete_file<P: AsRef<Path>>(&self, name: P) -> error::Result<()> {
220    let mut file_path = PathBuf::new();
221    file_path.push(&self.path);
222    file_path.push(name);
223    error::result_from_io(fs::remove_file(file_path))
224  }
225  /// delete a directory inside the directory
226  /// ```
227  /// dir.delete_dir("sub_dir")?;
228  /// ```
229  pub fn delete_dir<P: AsRef<Path>>(&self, name: P) -> error::Result<()> {
230    let mut file_path = PathBuf::new();
231    file_path.push(&self.path);
232    file_path.push(name);
233    error::result_from_fse(fs_extra::dir::remove(file_path))
234  }
235  /// get a fs_pro::File inside the directory
236  /// ```
237  /// let file = dir.get_file("my_file.txt")?;
238  /// file.create()?;
239  /// // ...
240  /// ```
241  pub fn get_file<P: AsRef<Path>>(&self, name: P) -> error::Result<file::File> {
242    let mut file_path = PathBuf::new();
243    file_path.push(&self.path);
244    file_path.push(name);
245    Ok(file::File::new(file_path)?)
246  }
247  /// get a fs_pro::Dir inside the directory
248  /// ```
249  /// let sub_dir = dir.get_dir("sub_dir")?;
250  /// sub_dir.create()?;
251  /// // ...
252  /// ```
253  pub fn get_dir<P: AsRef<Path>>(&self, name: P) -> error::Result<Dir> {
254    let mut file_path = PathBuf::new();
255    file_path.push(&self.path);
256    file_path.push(name);
257    Ok(Dir::new(file_path)?)
258  }
259  /// copy the directory and returns directory's copy fs_pro::Dir
260  /// ```
261  /// let dir_copy = dir.copy("copy_path")?;
262  /// dir_copy.create_file("some_file")?;
263  /// // ...
264  /// ```
265  pub fn copy<P: AsRef<Path>>(
266    &self,
267    to: P,
268    options: &fs_extra::dir::CopyOptions,
269  ) -> error::Result<Dir> {
270    let mut dest = PathBuf::new();
271    dest.push(to);
272    if !dest.exists() {
273      error::result_from_io(fs::create_dir(&dest))?;
274    }
275    error::result_from_fse(fs_extra::dir::copy(&self.path, &dest, options))?;
276    Ok(Dir::new(dest)?)
277  }
278  /// copy the directory with progress and returns directory's copy as fs_pro::Dir
279  /// ```
280  /// extern crate fs_extra;
281  /// use fs_extra::dir::{CopyOptions, TransitProcess, TransitProcessResult};
282  ///
283  /// let options = CopyOptions::new();
284  /// let handle = |process_info: TransitProcess|  {
285  ///    println!("{}", process_info.total_bytes);
286  ///    TransitProcessResult::ContinueOrAbort
287  /// }
288  /// let dir_copy = dir.copy_with_progress("dest_path", &options, handle)?;
289  /// ```
290  pub fn copy_with_progress<
291    P: AsRef<Path>,
292    F: FnMut(fs_extra::dir::TransitProcess) -> fs_extra::dir::TransitProcessResult,
293  >(
294    &self,
295    to: P,
296    options: &fs_extra::dir::CopyOptions,
297    progress_handler: F,
298  ) -> error::Result<Dir> {
299    let mut dest = PathBuf::new();
300    dest.push(to);
301    if !dest.exists() {
302      error::result_from_io(fs::create_dir(&dest))?;
303    }
304    error::result_from_fse(fs_extra::dir::copy_with_progress(
305      &self.path,
306      &dest,
307      options,
308      progress_handler,
309    ))?;
310    Ok(Dir::new(dest)?)
311  }
312  /// moves the directory and returns directory as fs_pro::Dir
313  /// ```
314  /// dir = dir.move("dest")?;
315  /// dir.create_file("some_file")?;
316  /// // ...
317  /// ```
318  pub fn move_to<P: AsRef<Path>>(
319    &self,
320    to: P,
321    options: &fs_extra::dir::CopyOptions,
322  ) -> error::Result<Dir> {
323    let mut dest = PathBuf::new();
324    dest.push(to);
325    if !dest.exists() {
326      error::result_from_io(fs::create_dir(&dest))?;
327    }
328    error::result_from_fse(fs_extra::dir::move_dir(&self.path, &dest, options))?;
329    Ok(Dir::new(dest)?)
330  }
331  /// copy the directory with progress and returns directory's dest as fs_pro::Dir
332  /// ```
333  /// extern crate fs_extra;
334  /// use fs_extra::dir::{CopyOptions, TransitProcess, TransitProcessResult};
335  ///
336  /// let options = CopyOptions::new();
337  /// let handle = |process_info: TransitProcess|  {
338  ///    println!("{}", process_info.total_bytes);
339  ///    TransitProcessResult::ContinueOrAbort
340  /// }
341  /// dir = dir.move_with_progress("dest_path", &options, handle)?;
342  /// ```
343  pub fn move_to_with_progress<
344    P: AsRef<Path>,
345    F: FnMut(fs_extra::dir::TransitProcess) -> fs_extra::dir::TransitProcessResult,
346  >(
347    &self,
348    to: P,
349    options: &fs_extra::dir::CopyOptions,
350    progress_handler: F,
351  ) -> error::Result<Dir> {
352    let mut dest = PathBuf::new();
353    dest.push(to);
354    if !dest.exists() {
355      error::result_from_io(fs::create_dir(&dest))?;
356    }
357    error::result_from_fse(fs_extra::dir::move_dir_with_progress(
358      &self.path,
359      &dest,
360      options,
361      progress_handler,
362    ))?;
363    Ok(Dir::new(dest)?)
364  }
365  /// return fs_extra::dir::DirContent which contains information about directory
366  /// see https://docs.rs/fs_extra/1.1.0/fs_extra/dir/fn.get_dir_content.html
367  /// ```
368  /// let dir_content = dir.get_content()?;
369  /// for directory in dir_content.directories {
370  ///   println!("{}", directory); // print directory path
371  /// }
372  /// ```
373  pub fn get_content(&self) -> error::Result<fs_extra::dir::DirContent> {
374    error::result_from_fse(fs_extra::dir::get_dir_content(&self.path))
375  }
376  /// return fs_extra::dir::DirContent which contains information about directory
377  /// see https://docs.rs/fs_extra/1.1.0/fs_extra/dir/fn.get_dir_content2.html
378  /// ```
379  /// extern crate fs_extra;
380  /// use fs_extra::dir::DirOptions;
381  ///
382  /// let options = DirOptions::new();
383  /// options.depth = 3; // Get 3 levels of folder.
384  /// let dir_content = get_dir_content2("dir", &options)?;
385  /// for directory in dir_content.directories {
386  ///    println!("{}", directory); // print directory path
387  /// }
388  /// ```  
389  pub fn get_content2(
390    &self,
391    options: &fs_extra::dir::DirOptions,
392  ) -> error::Result<fs_extra::dir::DirContent> {
393    error::result_from_fse(fs_extra::dir::get_dir_content2(&self.path, options))
394  }
395  /// returns information about directory entry with information which you choose in config
396  /// see https://docs.rs/fs_extra/1.1.0/fs_extra/dir/fn.get_details_entry.html
397  /// ```
398  /// extern crate fs_extra;
399  /// use fs_extra::dir::{DirEntryAttr};
400  /// use std::collections::{HashMap, HashSet};
401  ///
402  /// let mut config = HashSet::new();
403  /// config.insert(DirEntryAttr::Name);
404  /// config.insert(DirEntryAttr::Size);
405  ///
406  /// let entry_info = dir.get_details_entry(&config);
407  /// assert_eq!(2, entry_info.len());
408  /// ```
409  pub fn get_details_entry(
410    &self,
411    config: &HashSet<fs_extra::dir::DirEntryAttr>,
412  ) -> error::Result<HashMap<fs_extra::dir::DirEntryAttr, fs_extra::dir::DirEntryValue>> {
413    error::result_from_fse(fs_extra::dir::get_details_entry(&self.path, config))
414  }
415  /// creates a directory inside directory and it's parent if missing
416  /// ```
417  /// let sub_dir = dir.create_dir_all("foo/bar/some") // creates foo and bar and some
418  /// ```
419  pub fn create_dir_all<P: AsRef<Path>>(&self, path: P) -> error::Result<Dir> {
420    let mut path_buf = PathBuf::new();
421    path_buf.push(&self.path);
422    path_buf.push(path);
423    error::result_from_fse(fs_extra::dir::create_all(&path_buf, false))?;
424    Ok(Dir::new(path_buf)?)
425  }
426  /// creates a file inside directory and it's parent if missing
427  /// ```
428  /// let file = dir.create_file_all("foo/bar/some.txt") // creates foo and bar and some.txt
429  /// ```
430  pub fn create_file_all<P: AsRef<Path>>(&self, path: P) -> error::Result<file::File> {
431    let mut path_buf = PathBuf::new();
432    path_buf.push(&self.path);
433    path_buf.push(path);
434    let path_buf_parent =
435      error::result_from_option2(path_buf.parent(), error::ErrorKind::PathNoParentFound)?;
436    error::result_from_fse(fs_extra::dir::create_all(path_buf_parent, false))?;
437    let file = file::File::new(path_buf)?;
438    file.create()?;
439    Ok(file)
440  }
441  /// returns collection directory entries with information which you choose in config
442  /// see https://docs.rs/fs_extra/1.1.0/fs_extra/dir/fn.ls.html
443  /// ```
444  /// extern crate fs_extra;
445  /// use fs_extra::dir::DirEntryAttr;
446  /// use std::collections::HashSet;
447  /// let mut config = HashSet::new();
448  /// config.insert(DirEntryAttr::Name);
449  /// config.insert(DirEntryAttr::Size);
450  /// config.insert(DirEntryAttr::BaseInfo);
451  ///
452  /// let result = dir.ls(&config);
453  /// assert_eq!(2, ls_result.items.len());
454  /// assert_eq!(2, ls_result.base.len());
455  /// ```
456  pub fn ls(
457    &self,
458    config: &HashSet<fs_extra::dir::DirEntryAttr>,
459  ) -> error::Result<fs_extra::dir::LsResult> {
460    error::result_from_fse(fs_extra::dir::ls(&self.path, config))
461  }
462  /// reads the folder
463  /// ```
464  /// use fs_pro::DirEntry;
465  /// for entry in my_dir.read()? {
466  ///   match entry {
467  ///     DirEntry::File(file) => {
468  ///       println!("{:?} is a file", file.path)
469  ///     }
470  ///     DirEntry::Dir(dir) => {
471  ///       println!("{:?} is a folder", dir.path)
472  ///     }
473  ///   }
474  /// }
475  /// ```
476  pub fn read(&self) -> error::Result<Vec<DirEntry>> {
477    let mut result: Vec<DirEntry> = vec![];
478    for entry in error::result_from_io(fs::read_dir(self.path.as_path()))? {
479      let entry = error::result_from_io(entry)?;
480      if entry.path().is_file() {
481        result.push(DirEntry::File(File::new(entry.path())?));
482      } else {
483        result.push(DirEntry::Dir(Dir::new(entry.path())?));
484      }
485    }
486    Ok(result)
487  }
488  /// read the dir and return an array containing the file name of each entry as OsString
489  pub fn read_as_osstring_vec(&self) -> error::Result<Vec<OsString>> {
490    let mut result: Vec<OsString> = vec![];
491    for entry in error::result_from_io(fs::read_dir(self.path.as_path()))? {
492      let entry = error::result_from_io(entry)?;
493      result.push(entry.file_name());
494    }
495    Ok(result)
496  }
497  /// read the dir and return an array containing the full path of each entry as PathBuf
498  pub fn read_as_pathbuf_vec(&self) -> error::Result<Vec<PathBuf>> {
499    let mut result: Vec<PathBuf> = vec![];
500    for entry in error::result_from_io(fs::read_dir(self.path.as_path()))? {
501      let entry = error::result_from_io(entry)?;
502      result.push(entry.path());
503    }
504    Ok(result)
505  }
506  /// checks if an entry (file or folder) exists in the dir
507  pub fn entry_exists<P: AsRef<Path>>(&self, path: P) -> bool {
508    self.path.join(path).exists()
509  }
510}