ls_option/option.rs
1use std::{ffi::OsStr, path::Path};
2
3#[derive(Clone, Debug)]
4pub struct ListOption {
5 // if true, list directories
6 dir: bool,
7 // if true, list files
8 file: bool,
9 // if true, show hidden files
10 hidden: bool,
11 // if true,show unhidden files
12 unhidden: bool,
13 // if true, list recursively
14 recursive: bool,
15 // default 1, list only current directory
16 level: usize,
17 // if not empty, list only files with these extensions
18 sufs: Vec<String>,
19}
20// Default implementation for ListOption
21impl Default for ListOption {
22 fn default() -> Self {
23 Self {
24 dir: true,
25 file: true,
26 hidden: false,
27 unhidden: true,
28 recursive: false,
29 level: 1,
30 sufs: Vec::new(),
31 }
32 }
33}
34
35/// specify the list option
36impl ListOption {
37 pub fn new() -> Self {
38 Self::default()
39 }
40 /// set if allow this option to show directories
41 pub fn dir(&mut self, if_show: bool) -> &mut Self {
42 self.dir = if_show;
43 self
44 }
45
46 /// set if allow this option to show files
47 pub fn file(&mut self, if_show: bool) -> &mut Self {
48 self.file = if_show;
49 self
50 }
51
52 /// set if allow this option to show hidden files
53 pub fn hidden(&mut self, if_show: bool) -> &mut Self {
54 self.hidden = if_show;
55 self
56 }
57
58 /// set if allow this option to show unhidden files
59 pub fn unhidden(&mut self, if_show: bool) -> &mut Self {
60 self.unhidden = if_show;
61 self
62 }
63
64 /// set the level of recursion while listing files in some path
65 pub fn level(&mut self, level: usize) -> &mut Self {
66 self.level = level;
67 self
68 }
69
70 /// set if allow this option to list recursively
71 pub fn recursive(&mut self, if_choose: bool) -> &mut Self {
72 self.recursive = if_choose;
73 self
74 }
75
76 /// add one ext to the list of allowed extensions
77 ///
78 /// only files with one of these extensions will be listed
79 ///
80 /// e.g. ext("rs") will allow files with .rs extension to be listed
81 ///
82 /// e.g. ext("rs").ext("toml") will allow files with .rs and .toml extensions to be listed
83 pub fn ext(&mut self, ext: &str) -> &mut Self {
84 self.sufs.push(format!(".{}", ext));
85 self
86 }
87
88 /// add multiple exts to the list of allowed extensions
89 ///
90 /// only files with one of these extensions will be listed
91 ///
92 /// but this function will shadow the previous sufs
93 ///
94 /// e.g. exts(vec!["rs", "toml"]) will allow files with .rs and .toml extensions to be listed
95 ///
96 /// e.g. exts(vec!["rs"]).exts(vec!["toml"]) will only allow files with .toml extensions to be listed
97 ///
98 pub fn exts(&mut self, exts: Vec<&str>) -> &mut Self {
99 self.sufs = exts.iter().map(|s| format!(".{}", s)).collect();
100 self
101 }
102
103 /// add one suf to the list of allowed suffixes,
104 /// only files with one of these suffixes will be listed
105 ///
106 /// e.g. suf("rs") will allow files with rs suffix to be listed
107 ///
108 /// notice that exts function will shadow the previous sufs
109 ///
110 /// e.g. suf(".rs").ext("toml") will only allow files with .rs and .toml extensions to be listed
111 ///
112 /// e.g. suf(".rs").suf(".toml") will only allow files with .toml extensions to be listed
113 ///
114 /// e.g. suf("rs").exts(vec!["toml"]) will only allow files with .toml extensions to be listed
115 pub fn suf(&mut self, suf: &str) -> &mut Self {
116 self.sufs.push(suf.to_string());
117 self
118 }
119
120 /// add multiple sufs to the list of allowed suffixes
121 ///
122 /// only files with one of these suffixes will be listed
123 ///
124 /// but this function will shadow the previous sufs
125 ///
126 /// e.g. sufs(vec!["rs", "toml"]) will allow files with rs and toml suffixes to be listed
127 ///
128 /// e.g. sufs(vec![".rs"]).sufs(vec![".toml"]) will only allow files with .toml extensions to be listed
129 ///
130 /// e.g. sufs(vec![".rs"]).ext("toml") will allow files with .rs or .toml extension to be listed
131 ///
132 /// e.g. sufs(vec!["rs"]).exts(vec!["toml"]) will only allow files with .toml extensions to be listed
133 ///
134 pub fn sufs(&mut self, sufs: Vec<&str>) -> &mut Self {
135 self.sufs = sufs.iter().map(|s| s.to_string()).collect();
136 self
137 }
138}
139
140impl ListOption {
141 pub fn only_dir(&mut self) -> &mut Self {
142 self.file = false;
143 self.dir = true;
144 self
145 }
146 pub fn only_file(&mut self) -> &mut Self {
147 self.file = true;
148 self.dir = false;
149 self
150 }
151 pub fn only_hidden(&mut self) -> &mut Self {
152 self.hidden = true;
153 self.unhidden = false;
154 self
155 }
156 pub fn only_unhidden(&mut self) -> &mut Self {
157 self.hidden = false;
158 self.unhidden = true;
159 self
160 }
161}
162
163/// impl list functionality
164impl ListOption {
165 /// Lists the files and directories at the given path according to the options set in the ListOption
166 ///
167 /// if the path is a file, it will be listed if it matches the options set in the ListOption
168 ///
169 /// if the path is a directory, all files and directories in it will be listed if they match the options set in the ListOption
170 pub fn list<S>(&self, path: &S) -> Vec<String>
171 where
172 S: AsRef<OsStr> + ?Sized,
173 {
174 let mut ret: Vec<String> = Vec::new();
175 if self.level == 0 {
176 return ret;
177 }
178 let path = Path::new(path);
179 if self.would_show(path) {
180 ret.push(path.to_str().unwrap().to_string());
181 }
182 if path.is_file() {
183 return ret;
184 }
185 if path.is_dir() {
186 // if is a directory, list all files and directories in it
187 for entry in path.read_dir().unwrap() {
188 let entry = entry.unwrap();
189 let path = entry.path();
190 let mut sub_option = self.clone();
191 if !self.recursive {
192 sub_option.level = if self.level == 0 { 0 } else { self.level - 1 };
193 }
194 if self.would_show(&path) {
195 ret.push(path.to_str().unwrap().to_string());
196 }
197 ret.extend(sub_option.inner_list(&path));
198 }
199 }
200 ret
201 }
202 fn inner_list(&self, path: &Path) -> Vec<String> {
203 let mut ret: Vec<String> = Vec::new();
204 if self.level == 0 {
205 return ret;
206 }
207 if path.is_dir() {
208 // if is a directory, list all files and directories in it
209 for entry in path.read_dir().unwrap() {
210 let entry = entry.unwrap();
211 let path = entry.path();
212 let mut sub_option = self.clone();
213 if !self.recursive {
214 sub_option.level = if self.level == 0 { 0 } else { self.level - 1 };
215 }
216 if self.would_show(&path) {
217 ret.push(path.to_str().unwrap().to_string());
218 }
219 ret.extend(sub_option.inner_list(&path));
220 }
221 }
222 ret
223 }
224
225 /// check if the path would be shown according to the options set in the ListOption
226 pub fn would_show<S>(&self, path: &S) -> bool
227 where
228 S: AsRef<OsStr> + ?Sized,
229 {
230 let check_hidden = |path: &Path| {
231 let base_name = path.file_name().unwrap().to_str().unwrap();
232 if self.hidden && base_name.starts_with('.') {
233 true
234 } else {
235 self.unhidden && !base_name.starts_with('.')
236 }
237 };
238 let check_file_dir =
239 |path: &Path| (path.is_file() && self.file) || (path.is_dir() && self.dir);
240 let check_level = || self.recursive || self.level > 0;
241 let check_ext = |path: &Path| {
242 self.sufs.is_empty()
243 || self
244 .sufs
245 .iter()
246 .any(|suf| path.to_str().unwrap().ends_with(suf))
247 };
248 let path = Path::new(path);
249 if !path.exists() {
250 return false;
251 }
252 let path = &path.canonicalize().unwrap();
253 path.exists()
254 && check_hidden(path)
255 && check_file_dir(path)
256 && check_level()
257 && check_ext(path)
258 }
259}