assembly_pack/common/
fs.rs

1//! # Tools to handle a file system
2use std::{
3    fs::{DirEntry, Metadata},
4    io,
5    path::Path,
6};
7
8/// Information on a file
9pub struct FileInfo<'a> {
10    _path: &'a str,
11    _name: String,
12    _real: &'a Path,
13    _entry: DirEntry,
14}
15
16impl<'a> FileInfo<'a> {
17    /// Return just the filename
18    pub fn name(&self) -> &str {
19        self._name.as_ref()
20    }
21
22    /// return the full "local" path
23    pub fn path(&self) -> String {
24        win_join(self._path, &self._name)
25    }
26
27    /// Return the "real" path
28    pub fn real(&self) -> &Path {
29        self._real
30    }
31
32    /// Return the metadata for this file
33    pub fn metadata(&self) -> io::Result<Metadata> {
34        self._entry.metadata()
35    }
36}
37
38// Join a windows path to a prefix
39fn win_join(base: &str, name: &str) -> String {
40    if base.is_empty() {
41        name.to_string()
42    } else if base.ends_with('\\') {
43        format!("{}{}", base, name)
44    } else {
45        format!("{}\\{}", base, name)
46    }
47}
48
49/// A trait to scan a directory of files
50pub trait FsVisitor {
51    /// Called when a file is visited
52    fn visit_file(&mut self, info: FileInfo);
53
54    /// Called when read-dir fails
55    #[allow(unused_variables)]
56    fn failed_read_dir(&mut self, real: &Path, e: io::Error) {
57        #[cfg(feature = "log")]
58        log::error!("Failed to read_dir {}: {}", real.display(), e);
59    }
60
61    /// Called when read-dir fails
62    #[allow(unused_variables)]
63    fn failed_next_dir_entry(&mut self, real: &Path, e: io::Error) {
64        #[cfg(feature = "log")]
65        log::error!("Failed next dir entry {}: {}", real.display(), e);
66    }
67}
68
69/// Scan a directory and call [FsVisitor::visit_file] for all files
70///
71/// ## Parameters
72///
73/// - *path*: a relative path with windows-style separators (i.e `\`)
74/// - *read*: the real path of the directory
75/// - *recurse*: Whether to recurse into subdirectories
76pub fn scan_dir<V: FsVisitor>(visitor: &mut V, path: String, real: &Path, recurse: bool) {
77    let rd = match std::fs::read_dir(real) {
78        Ok(rd) => rd,
79        Err(e) => {
80            visitor.failed_read_dir(real, e);
81            return;
82        }
83    };
84
85    // collect all entries
86    for e in rd {
87        match e {
88            Ok(_entry) => {
89                let new_real_path = _entry.path();
90                let name = new_real_path
91                    .file_name()
92                    .expect("file_name on dir entry path")
93                    .to_string_lossy()
94                    .into_owned();
95                match _entry.file_type() {
96                    Ok(t) => {
97                        if t.is_file() {
98                            visitor.visit_file(FileInfo {
99                                _path: &path,
100                                _name: name,
101                                _real: &new_real_path,
102                                _entry,
103                            });
104                        } else if t.is_dir() && recurse {
105                            let new_path = win_join(&path, &name);
106                            scan_dir(visitor, new_path, &new_real_path, recurse);
107                        }
108                    }
109                    Err(e) => visitor.failed_next_dir_entry(&new_real_path, e),
110                }
111            }
112            Err(e) => {
113                visitor.failed_next_dir_entry(real, e);
114                return;
115            }
116        };
117    }
118}