1use crate::cli::cli::Cli;
2use crate::cli::file::FileKind;
3use crate::cli::hidden::HiddenKind;
4use crate::cli::order::OrderKind;
5use crate::cli::recent::RecentKind;
6use crate::error::MyResult;
7use crate::git::flags::GitFlags;
8use chrono::{DateTime, TimeZone, Utc};
9use clap_builder::{ColorChoice, Parser};
10use clap_complete::Shell;
11use std::collections::HashSet;
12
13#[derive(Default)]
14pub struct Config {
15 cli: Cli,
16 piped: bool,
17 curr_time: DateTime<Utc>,
18 sort_order: Vec<OrderKind>,
19 sort_name: bool,
20}
21
22impl Config {
23 pub fn new<G: Fn() -> DateTime<Utc>>(
24 args: Vec<String>,
25 piped: bool,
26 factory: G,
27 ) -> MyResult<Self> {
28 let mut cli = Cli::try_parse_from(args)?;
29 let piped = piped && !cli.terminal();
30 let curr_time = cli.curr_time(factory);
31 let (sort_order, sort_name) = cli.sort_order();
32 let config = Self { cli, piped, curr_time, sort_order, sort_name };
33 Ok(config)
34 }
35
36 pub fn curr_time(&self) -> &DateTime<Utc> {
37 &self.curr_time
38 }
39
40 pub fn min_depth(&self) -> Option<usize> {
41 self.cli.min_depth()
42 }
43
44 pub fn max_depth(&self) -> Option<usize> {
45 self.cli.max_depth()
46 }
47
48 pub fn show_indent(&self) -> bool {
49 self.cli.show_indent
50 }
51
52 pub fn show_hidden(&self) -> HiddenKind {
53 match self.cli.show_hidden {
54 0 => HiddenKind::None,
55 1 => HiddenKind::Files,
56 _ => HiddenKind::Recurse,
57 }
58 }
59
60 pub fn zip_expand(&self) -> bool {
61 self.cli.zip_expand
62 }
63
64 pub fn zip_password(&self) -> &Option<String> {
65 &self.cli.zip_password
66 }
67
68 pub fn case_sensitive(&self) -> Option<bool> {
69 if self.cli.case_sensitive {
70 Some(true)
71 } else if self.cli.case_insensitive {
72 Some(false)
73 } else {
74 None
75 }
76 }
77
78 pub fn sort_order(&self) -> &Vec<OrderKind> {
79 &self.sort_order
80 }
81
82 pub fn sort_name(&self) -> bool {
83 self.sort_name
84 }
85
86 pub fn filter_recent(&self) -> RecentKind {
87 self.cli.filter_recent.unwrap_or_default()
88 }
89
90 pub fn filter_types(&self) -> Option<&HashSet<FileKind>> {
91 self.cli.filter_types.as_ref().map(|x| &x.inner)
92 }
93
94 pub fn filter_git(&self) -> Option<&GitFlags> {
95 self.cli.filter_git.as_ref()
96 }
97
98 #[cfg(debug_assertions)]
99 pub fn show_debug(&self) -> bool {
100 self.cli.show_debug
101 }
102
103 pub fn show_precise(&self) -> bool {
104 self.cli.show_precise
105 }
106
107 pub fn show_utc(&self) -> bool {
108 self.cli.show_utc
109 }
110
111 pub fn show_total(&self) -> bool {
112 self.cli.show_total
113 }
114
115 #[cfg(unix)]
116 pub fn show_owner(&self) -> bool {
117 self.cli.show_owner
118 }
119
120 pub fn show_crc(&self) -> bool {
121 self.cli.show_crc
122 }
123
124 pub fn show_sig(&self) -> bool {
125 self.cli.show_sig
126 }
127
128 pub fn only_path(&self) -> bool {
129 if self.cli.null_path {
130 true
131 } else {
132 match self.cli.only_path {
133 0 => self.piped,
134 1 => true,
135 _ => false,
136 }
137 }
138 }
139
140 pub fn escape_path(&self) -> bool {
141 if self.cli.null_path {
142 false
143 } else {
144 match self.cli.only_path {
145 0 => self.piped,
146 1 => false,
147 _ => false,
148 }
149 }
150 }
151
152 pub fn null_path(&self) -> bool {
153 self.cli.null_path
154 }
155
156 pub fn abs_path(&self) -> bool {
157 self.cli.abs_path
158 }
159
160 #[cfg(windows)]
161 pub fn win_path(&self) -> bool {
162 self.cli.win_path
163 }
164
165 #[cfg(windows)]
166 pub fn win_ver(&self) -> bool {
167 self.cli.win_ver
168 }
169
170 pub fn color(&self) -> Option<ColorChoice> {
171 self.cli.color
172 }
173
174 pub fn quiet(&self) -> bool {
175 self.cli.quiet
176 }
177
178 pub fn completion(&self) -> Option<Shell> {
179 self.cli.completion
180 }
181
182 pub fn patterns(&self) -> &Vec<String> {
183 &self.cli.patterns
184 }
185
186 pub fn start_time<Tz: TimeZone>(&self, zone: &Tz) -> Option<DateTime<Utc>> {
187 self.filter_recent().subtract_from(self.curr_time(), zone)
188 }
189
190 #[cfg(windows)]
191 pub fn want_decrypt(&self) -> bool {
192 self.show_sig() || self.win_ver()
193 }
194
195 #[cfg(not(windows))]
196 pub fn want_decrypt(&self) -> bool {
197 self.show_sig()
198 }
199}
200
201#[cfg(test)]
202mod tests {
203 use crate::cli::file::{ExecKind, FileKind, FileSet};
204 use crate::cli::hidden::HiddenKind;
205 use crate::cli::order::DirKind;
206 use crate::cli::recent::RecentKind;
207 use crate::config::{Config, OrderKind};
208 use crate::git::flags::GitFlags;
209 use chrono::{DateTime, NaiveDate, NaiveDateTime, NaiveTime, TimeZone, Utc};
210 use pretty_assertions::assert_eq;
211 use std::collections::HashSet;
212 use std::hash::Hash;
213
214 #[test]
215 fn test_depth_is_handled() {
216 let args = vec!["ex"];
217 let config = create_config(args);
218 assert_eq!(Some(1), config.min_depth());
219 assert_eq!(Some(1), config.max_depth());
220
221 let args = vec!["ex", "-d4"];
222 let config = create_config(args);
223 assert_eq!(Some(1), config.min_depth());
224 assert_eq!(Some(5), config.max_depth());
225
226 let args = vec!["ex", "-d-4"];
227 let config = create_config(args);
228 assert_eq!(Some(1), config.min_depth());
229 assert_eq!(Some(5), config.max_depth());
230
231 let args = vec!["ex", "-d2-4"];
232 let config = create_config(args);
233 assert_eq!(Some(3), config.min_depth());
234 assert_eq!(Some(5), config.max_depth());
235
236 let args = vec!["ex", "-d2-"];
237 let config = create_config(args);
238 assert_eq!(Some(3), config.min_depth());
239 assert_eq!(None, config.max_depth());
240
241 let args = vec!["ex", "-s"];
242 let config = create_config(args);
243 assert_eq!(Some(1), config.min_depth());
244 assert_eq!(None, config.max_depth());
245
246 let args = vec!["ex", "-s", "-d4"];
247 let config = create_config(args);
248 assert_eq!(Some(1), config.min_depth());
249 assert_eq!(None, config.max_depth());
250
251 let args = vec!["ex", "--recurse"];
252 let config = create_config(args);
253 assert_eq!(Some(1), config.min_depth());
254 assert_eq!(None, config.max_depth());
255
256 let args = vec!["ex", "--depth=4"];
257 let config = create_config(args);
258 assert_eq!(Some(1), config.min_depth());
259 assert_eq!(Some(5), config.max_depth());
260 }
261
262 #[test]
263 fn test_indent_is_handled() {
264 let args = vec!["ex"];
265 let config = create_config(args);
266 assert_eq!(false, config.show_indent());
267
268 let args = vec!["ex", "-i"];
269 let config = create_config(args);
270 assert_eq!(true, config.show_indent());
271
272 let args = vec!["ex", "--indent"];
273 let config = create_config(args);
274 assert_eq!(true, config.show_indent());
275 }
276
277 #[test]
278 fn test_all_files_is_handled() {
279 let args = vec!["ex"];
280 let config = create_config(args);
281 assert_eq!(HiddenKind::None, config.show_hidden());
282
283 let args = vec!["ex", "-a"];
284 let config = create_config(args);
285 assert_eq!(HiddenKind::Files, config.show_hidden());
286
287 let args = vec!["ex", "-aa"];
288 let config = create_config(args);
289 assert_eq!(HiddenKind::Recurse, config.show_hidden());
290
291 let args = vec!["ex", "-a", "-a"];
292 let config = create_config(args);
293 assert_eq!(HiddenKind::Recurse, config.show_hidden());
294
295 let args = vec!["ex", "--all-files"];
296 let config = create_config(args);
297 assert_eq!(HiddenKind::Files, config.show_hidden());
298 }
299
300 #[test]
301 fn test_zip_is_handled() {
302 let args = vec!["ex"];
303 let config = create_config(args);
304 assert_eq!(false, config.zip_expand());
305
306 let args = vec!["ex", "-z"];
307 let config = create_config(args);
308 assert_eq!(true, config.zip_expand());
309
310 let args = vec!["ex", "--zip"];
311 let config = create_config(args);
312 assert_eq!(true, config.zip_expand());
313 }
314
315 #[test]
316 fn test_password_is_handled() {
317 let args = vec!["ex"];
318 let config = create_config(args);
319 assert_eq!(&None, config.zip_password());
320
321 let args = vec!["ex", "--password", "secret"];
322 let config = create_config(args);
323 assert_eq!(&Some(String::from("secret")), config.zip_password());
324 }
325
326 #[test]
327 fn test_case_is_handled() {
328 let args = vec!["ex"];
329 let config = create_config(args);
330 assert_eq!(None, config.case_sensitive());
331
332 let args = vec!["ex", "--case"];
333 let config = create_config(args);
334 assert_eq!(Some(true), config.case_sensitive());
335
336 let args = vec!["ex", "--case", "--no-case"];
337 let config = create_config(args);
338 assert_eq!(Some(false), config.case_sensitive());
339
340 let args = vec!["ex", "--no-case"];
341 let config = create_config(args);
342 assert_eq!(Some(false), config.case_sensitive());
343
344 let args = vec!["ex", "--no-case", "--case"];
345 let config = create_config(args);
346 assert_eq!(Some(true), config.case_sensitive());
347 }
348
349 #[test]
350 fn test_order_is_handled() {
351 let expected = vec![OrderKind::Group, OrderKind::Dir, OrderKind::Name];
352 let args = vec!["ex"];
353 let config = create_config(args);
354 assert_eq!(&expected, config.sort_order());
355 assert_eq!(false, config.sort_name());
356
357 let expected = vec![OrderKind::Dir, OrderKind::Name];
358 let args = vec!["ex", "-od"];
359 let config = create_config(args);
360 assert_eq!(&expected, config.sort_order());
361 assert_eq!(false, config.sort_name());
362
363 let expected = vec![OrderKind::Name, OrderKind::Dir];
364 let args = vec!["ex", "-on"];
365 let config = create_config(args);
366 assert_eq!(&expected, config.sort_order());
367 assert_eq!(true, config.sort_name());
368
369 let expected = vec![OrderKind::Ext, OrderKind::Dir, OrderKind::Name];
370 let args = vec!["ex", "-oe"];
371 let config = create_config(args);
372 assert_eq!(&expected, config.sort_order());
373 assert_eq!(false, config.sort_name());
374
375 let expected = vec![OrderKind::Size(DirKind::Asc), OrderKind::Dir, OrderKind::Name];
376 let args = vec!["ex", "-os"];
377 let config = create_config(args);
378 assert_eq!(&expected, config.sort_order());
379 assert_eq!(false, config.sort_name());
380
381 let expected = vec![OrderKind::Size(DirKind::Asc), OrderKind::Dir, OrderKind::Name];
382 let args = vec!["ex", "-os+"];
383 let config = create_config(args);
384 assert_eq!(&expected, config.sort_order());
385 assert_eq!(false, config.sort_name());
386
387 let expected = vec![OrderKind::Size(DirKind::Desc), OrderKind::Dir, OrderKind::Name];
388 let args = vec!["ex", "-os-"];
389 let config = create_config(args);
390 assert_eq!(&expected, config.sort_order());
391 assert_eq!(false, config.sort_name());
392
393 let expected = vec![OrderKind::Time(DirKind::Asc), OrderKind::Dir, OrderKind::Name];
394 let args = vec!["ex", "-ot"];
395 let config = create_config(args);
396 assert_eq!(&expected, config.sort_order());
397 assert_eq!(false, config.sort_name());
398
399 let expected = vec![OrderKind::Time(DirKind::Asc), OrderKind::Dir, OrderKind::Name];
400 let args = vec!["ex", "-ot+"];
401 let config = create_config(args);
402 assert_eq!(&expected, config.sort_order());
403 assert_eq!(false, config.sort_name());
404
405 let expected = vec![OrderKind::Time(DirKind::Desc), OrderKind::Dir, OrderKind::Name];
406 let args = vec!["ex", "-ot-"];
407 let config = create_config(args);
408 assert_eq!(&expected, config.sort_order());
409 assert_eq!(false, config.sort_name());
410
411 let expected = vec![OrderKind::Dir, OrderKind::Ext, OrderKind::Size(DirKind::Asc), OrderKind::Time(DirKind::Asc), OrderKind::Name];
412 let args = vec!["ex", "--order=dest"];
413 let config = create_config(args);
414 assert_eq!(&expected, config.sort_order());
415 assert_eq!(false, config.sort_name());
416
417 let expected = vec![OrderKind::Ext, OrderKind::Size(DirKind::Asc), OrderKind::Time(DirKind::Asc), OrderKind::Dir, OrderKind::Name];
418 let args = vec!["ex", "--order=est"];
419 let config = create_config(args);
420 assert_eq!(&expected, config.sort_order());
421 assert_eq!(false, config.sort_name());
422
423 let expected = vec![OrderKind::Ext, OrderKind::Size(DirKind::Asc), OrderKind::Time(DirKind::Asc), OrderKind::Dir, OrderKind::Name];
424 let args = vec!["ex", "--order=e+s+t+"];
425 let config = create_config(args);
426 assert_eq!(&expected, config.sort_order());
427 assert_eq!(false, config.sort_name());
428
429 let expected = vec![OrderKind::Ext, OrderKind::Size(DirKind::Desc), OrderKind::Time(DirKind::Desc), OrderKind::Dir, OrderKind::Name];
430 let args = vec!["ex", "--order=e-s-t-"];
431 let config = create_config(args);
432 assert_eq!(&expected, config.sort_order());
433 assert_eq!(false, config.sort_name());
434 }
435
436 #[test]
437 fn test_order_defaults_to_group_with_no_recursion() {
438 let expected = vec![OrderKind::Group, OrderKind::Dir, OrderKind::Name];
439 let args = vec!["ex"];
440 let config = create_config(args);
441 assert_eq!(&expected, config.sort_order());
442 assert_eq!(false, config.sort_name());
443
444 let expected = vec![OrderKind::Group, OrderKind::Dir, OrderKind::Name];
445 let args = vec!["ex", "-d0"];
446 let config = create_config(args);
447 assert_eq!(&expected, config.sort_order());
448 assert_eq!(false, config.sort_name());
449
450 let expected = vec![OrderKind::Dir, OrderKind::Name];
451 let args = vec!["ex", "-d1"];
452 let config = create_config(args);
453 assert_eq!(&expected, config.sort_order());
454 assert_eq!(false, config.sort_name());
455
456 let expected = vec![OrderKind::Dir, OrderKind::Name];
457 let args = vec!["ex", "-d2"];
458 let config = create_config(args);
459 assert_eq!(&expected, config.sort_order());
460 assert_eq!(false, config.sort_name());
461
462 let expected = vec![OrderKind::Dir, OrderKind::Name];
463 let args = vec!["ex", "-s"];
464 let config = create_config(args);
465 assert_eq!(&expected, config.sort_order());
466 assert_eq!(false, config.sort_name());
467
468 let expected = vec![OrderKind::Ext, OrderKind::Dir, OrderKind::Name];
469 let args = vec!["ex", "-oe"];
470 let config = create_config(args);
471 assert_eq!(&expected, config.sort_order());
472 assert_eq!(false, config.sort_name());
473
474 let expected = vec![OrderKind::Ext, OrderKind::Dir, OrderKind::Name];
475 let args = vec!["ex", "-oe", "-d0"];
476 let config = create_config(args);
477 assert_eq!(&expected, config.sort_order());
478 assert_eq!(false, config.sort_name());
479
480 let expected = vec![OrderKind::Ext, OrderKind::Dir, OrderKind::Name];
481 let args = vec!["ex", "-oe", "-d1"];
482 let config = create_config(args);
483 assert_eq!(&expected, config.sort_order());
484 assert_eq!(false, config.sort_name());
485
486 let expected = vec![OrderKind::Ext, OrderKind::Dir, OrderKind::Name];
487 let args = vec!["ex", "-oe", "-d2"];
488 let config = create_config(args);
489 assert_eq!(&expected, config.sort_order());
490 assert_eq!(false, config.sort_name());
491
492 let expected = vec![OrderKind::Ext, OrderKind::Dir, OrderKind::Name];
493 let args = vec!["ex", "-oe", "-s"];
494 let config = create_config(args);
495 assert_eq!(&expected, config.sort_order());
496 assert_eq!(false, config.sort_name());
497 }
498
499 #[test]
500 fn test_order_defaults_to_directory_with_indent() {
501 let expected = vec![OrderKind::Dir, OrderKind::Name];
502 let args = vec!["ex", "-i"];
503 let config = create_config(args);
504 assert_eq!(&expected, config.sort_order());
505 assert_eq!(false, config.sort_name());
506
507 let expected = vec![OrderKind::Dir, OrderKind::Name];
508 let args = vec!["ex", "-i", "-d0"];
509 let config = create_config(args);
510 assert_eq!(&expected, config.sort_order());
511 assert_eq!(false, config.sort_name());
512
513 let expected = vec![OrderKind::Dir, OrderKind::Name];
514 let args = vec!["ex", "-i", "-d1"];
515 let config = create_config(args);
516 assert_eq!(&expected, config.sort_order());
517 assert_eq!(false, config.sort_name());
518
519 let expected = vec![OrderKind::Dir, OrderKind::Name];
520 let args = vec!["ex", "-i", "-d2"];
521 let config = create_config(args);
522 assert_eq!(&expected, config.sort_order());
523 assert_eq!(false, config.sort_name());
524
525 let expected = vec![OrderKind::Dir, OrderKind::Name];
526 let args = vec!["ex", "-i", "-s"];
527 let config = create_config(args);
528 assert_eq!(&expected, config.sort_order());
529 assert_eq!(false, config.sort_name());
530
531 let expected = vec![OrderKind::Dir, OrderKind::Ext, OrderKind::Name];
532 let args = vec!["ex", "-i", "-oe"];
533 let config = create_config(args);
534 assert_eq!(&expected, config.sort_order());
535 assert_eq!(false, config.sort_name());
536
537 let expected = vec![OrderKind::Dir, OrderKind::Ext, OrderKind::Name];
538 let args = vec!["ex", "-i", "-oe", "-d0"];
539 let config = create_config(args);
540 assert_eq!(&expected, config.sort_order());
541 assert_eq!(false, config.sort_name());
542
543 let expected = vec![OrderKind::Dir, OrderKind::Ext, OrderKind::Name];
544 let args = vec!["ex", "-i", "-oe", "-d1"];
545 let config = create_config(args);
546 assert_eq!(&expected, config.sort_order());
547 assert_eq!(false, config.sort_name());
548
549 let expected = vec![OrderKind::Dir, OrderKind::Ext, OrderKind::Name];
550 let args = vec!["ex", "-i", "-oe", "-d2"];
551 let config = create_config(args);
552 assert_eq!(&expected, config.sort_order());
553 assert_eq!(false, config.sort_name());
554
555 let expected = vec![OrderKind::Dir, OrderKind::Ext, OrderKind::Name];
556 let args = vec!["ex", "-i", "-oe", "-s"];
557 let config = create_config(args);
558 assert_eq!(&expected, config.sort_order());
559 assert_eq!(false, config.sort_name());
560 }
561
562 #[test]
563 fn test_recent_is_handled() {
564 let args = vec!["ex"];
565 let config = create_config(args);
566 assert_eq!(RecentKind::None, config.filter_recent());
567
568 let args = vec!["ex", "-ry10"];
569 let config = create_config(args);
570 assert_eq!(RecentKind::Year(10), config.filter_recent());
571
572 let args = vec!["ex", "-rm6"];
573 let config = create_config(args);
574 assert_eq!(RecentKind::Month(6), config.filter_recent());
575
576 let args = vec!["ex", "-rw2"];
577 let config = create_config(args);
578 assert_eq!(RecentKind::Week(2), config.filter_recent());
579
580 let args = vec!["ex", "-rd"];
581 let config = create_config(args);
582 assert_eq!(RecentKind::Day(1), config.filter_recent());
583
584 let args = vec!["ex", "--recent=h"];
585 let config = create_config(args);
586 assert_eq!(RecentKind::Hour(1), config.filter_recent());
587
588 let args = vec!["ex", "--recent=H"];
589 let config = create_config(args);
590 assert_eq!(RecentKind::Hour(1), config.filter_recent());
591
592 let args = vec!["ex", "--recent=M5"];
593 let config = create_config(args);
594 assert_eq!(RecentKind::Min(5), config.filter_recent());
595
596 let args = vec!["ex", "--recent=S10"];
597 let config = create_config(args);
598 assert_eq!(RecentKind::Sec(10), config.filter_recent());
599 }
600
601 #[test]
602 fn test_none_is_subtracted_from_time() {
603 let now = create_time(2023, 7, 1, 0, 0, 0);
604 assert_eq!(None, RecentKind::None.subtract_from(&now, &Utc));
605 }
606
607 #[test]
608 fn test_second_is_subtracted_from_time() {
609 let now = create_time(2023, 7, 1, 0, 0, 0);
610 assert_eq!(Some(create_time(2023, 7, 1, 0, 0, 0)), RecentKind::Sec(0).subtract_from(&now, &Utc));
611 assert_eq!(Some(create_time(2023, 6, 30, 23, 59, 59)), RecentKind::Sec(1).subtract_from(&now, &Utc));
612 assert_eq!(Some(create_time(2023, 6, 30, 23, 59, 58)), RecentKind::Sec(2).subtract_from(&now, &Utc));
613 }
614
615 #[test]
616 fn test_minute_is_subtracted_from_time() {
617 let now = create_time(2023, 7, 1, 0, 0, 0);
618 assert_eq!(Some(create_time(2023, 7, 1, 0, 0, 0)), RecentKind::Min(0).subtract_from(&now, &Utc));
619 assert_eq!(Some(create_time(2023, 6, 30, 23, 59, 0)), RecentKind::Min(1).subtract_from(&now, &Utc));
620 assert_eq!(Some(create_time(2023, 6, 30, 23, 58, 0)), RecentKind::Min(2).subtract_from(&now, &Utc));
621 }
622
623 #[test]
624 fn test_hour_is_subtracted_from_time() {
625 let now = create_time(2023, 7, 1, 0, 0, 0);
626 assert_eq!(Some(create_time(2023, 7, 1, 0, 0, 0)), RecentKind::Hour(0).subtract_from(&now, &Utc));
627 assert_eq!(Some(create_time(2023, 6, 30, 23, 0, 0)), RecentKind::Hour(1).subtract_from(&now, &Utc));
628 assert_eq!(Some(create_time(2023, 6, 30, 22, 0, 0)), RecentKind::Hour(2).subtract_from(&now, &Utc));
629 }
630
631 #[test]
632 fn test_day_is_subtracted_from_time() {
633 let now = create_time(2023, 7, 1, 0, 0, 0);
634 assert_eq!(Some(create_time(2023, 7, 1, 0, 0, 0)), RecentKind::Day(0).subtract_from(&now, &Utc));
635 assert_eq!(Some(create_time(2023, 6, 30, 0, 0, 0)), RecentKind::Day(1).subtract_from(&now, &Utc));
636 assert_eq!(Some(create_time(2023, 6, 29, 0, 0, 0)), RecentKind::Day(2).subtract_from(&now, &Utc));
637 }
638
639 #[test]
640 fn test_week_is_subtracted_from_time() {
641 let now = create_time(2023, 7, 1, 0, 0, 0);
642 assert_eq!(Some(create_time(2023, 7, 1, 0, 0, 0)), RecentKind::Week(0).subtract_from(&now, &Utc));
643 assert_eq!(Some(create_time(2023, 6, 24, 0, 0, 0)), RecentKind::Week(1).subtract_from(&now, &Utc));
644 assert_eq!(Some(create_time(2023, 6, 17, 0, 0, 0)), RecentKind::Week(2).subtract_from(&now, &Utc));
645 }
646
647 #[test]
648 fn test_month_is_subtracted_from_time() {
649 let now = create_time(2023, 3, 31, 0, 0, 0);
650 assert_eq!(Some(create_time(2023, 3, 31, 0, 0, 0)), RecentKind::Month(0).subtract_from(&now, &Utc));
651 assert_eq!(Some(create_time(2023, 3, 1, 0, 0, 0)), RecentKind::Month(1).subtract_from(&now, &Utc));
652 assert_eq!(Some(create_time(2023, 1, 31, 0, 0, 0)), RecentKind::Month(2).subtract_from(&now, &Utc));
653 assert_eq!(Some(create_time(2022, 12, 31, 0, 0, 0)), RecentKind::Month(3).subtract_from(&now, &Utc));
654 assert_eq!(Some(create_time(2022, 12, 1, 0, 0, 0)), RecentKind::Month(4).subtract_from(&now, &Utc));
655 assert_eq!(Some(create_time(2022, 10, 31, 0, 0, 0)), RecentKind::Month(5).subtract_from(&now, &Utc));
656 }
657
658 #[test]
659 fn test_year_is_subtracted_from_time() {
660 let now = create_time(2024, 2, 29, 0, 0, 0);
661 assert_eq!(Some(create_time(2024, 2, 29, 0, 0, 0)), RecentKind::Year(0).subtract_from(&now, &Utc));
662 assert_eq!(Some(create_time(2023, 3, 1, 0, 0, 0)), RecentKind::Year(1).subtract_from(&now, &Utc));
663 assert_eq!(Some(create_time(2022, 3, 1, 0, 0, 0)), RecentKind::Year(2).subtract_from(&now, &Utc));
664 assert_eq!(Some(create_time(2021, 3, 1, 0, 0, 0)), RecentKind::Year(3).subtract_from(&now, &Utc));
665 assert_eq!(Some(create_time(2020, 2, 29, 0, 0, 0)), RecentKind::Year(4).subtract_from(&now, &Utc));
666 }
667
668 #[test]
669 fn test_type_is_handled() {
670 let args = vec!["ex"];
671 let config = create_config(args);
672 assert_eq!(None, config.filter_types());
673
674 let args = vec!["ex", "-tf"];
675 let config = create_config(args);
676 let expected = create_set(&[
677 FileKind::File(ExecKind::None),
678 FileKind::File(ExecKind::User),
679 FileKind::File(ExecKind::Other),
680 ]);
681 assert_eq!(Some(&expected), config.filter_types());
682
683 let args = vec!["ex", "-te"];
684 let config = create_config(args);
685 let expected = create_set(&[
686 FileKind::File(ExecKind::User),
687 FileKind::File(ExecKind::Other),
688 ]);
689 assert_eq!(Some(&expected), config.filter_types());
690
691 let args = vec!["ex", "-td"];
692 let config = create_config(args);
693 let expected = create_set(&[FileKind::Dir]);
694 assert_eq!(Some(&expected), config.filter_types());
695
696 let args = vec!["ex", "-tfd"];
697 let config = create_config(args);
698 let expected = create_set(&[
699 FileKind::File(ExecKind::None),
700 FileKind::File(ExecKind::User),
701 FileKind::File(ExecKind::Other),
702 FileKind::Dir,
703 ]);
704 assert_eq!(Some(&expected), config.filter_types());
705
706 let args = vec!["ex", "--type=l"];
707 let config = create_config(args);
708 let expected = create_set(&[
709 FileKind::Link(false),
710 FileKind::Link(true),
711 ]);
712 assert_eq!(Some(&expected), config.filter_types());
713 }
714
715 #[test]
716 fn test_git_is_handled() {
717 let args = vec!["ex"];
718 let config = create_config(args);
719 assert_eq!(None, config.filter_git());
720
721 let expected = GitFlags::default().with_everything(true);
722 let args = vec!["ex", "-gx"];
723 let config = create_config(args);
724 assert_eq!(Some(&expected), config.filter_git());
725
726 let expected = GitFlags::default().with_cached(true);
727 let args = vec!["ex", "-gc"];
728 let config = create_config(args);
729 assert_eq!(Some(&expected), config.filter_git());
730
731 let expected = GitFlags::default().with_added(true);
732 let args = vec!["ex", "-ga"];
733 let config = create_config(args);
734 assert_eq!(Some(&expected), config.filter_git());
735
736 let expected = GitFlags::default().with_modified(true);
737 let args = vec!["ex", "-gm"];
738 let config = create_config(args);
739 assert_eq!(Some(&expected), config.filter_git());
740
741 let expected = GitFlags::default().with_renamed(true);
742 let args = vec!["ex", "-gr"];
743 let config = create_config(args);
744 assert_eq!(Some(&expected), config.filter_git());
745
746 let expected = GitFlags::default().with_untracked(true);
747 let args = vec!["ex", "-gu"];
748 let config = create_config(args);
749 assert_eq!(Some(&expected), config.filter_git());
750
751 let expected = GitFlags::default().with_ignored(true);
752 let args = vec!["ex", "-gi"];
753 let config = create_config(args);
754 assert_eq!(Some(&expected), config.filter_git());
755
756 let expected = GitFlags::default().with_added(true).with_modified(true).with_renamed(true);
757 let args = vec!["ex", "--git=amr"];
758 let config = create_config(args);
759 assert_eq!(Some(&expected), config.filter_git());
760 }
761
762 #[test]
763 fn test_precise_is_handled() {
764 let args = vec!["ex"];
765 let config = create_config(args);
766 assert_eq!(false, config.show_precise());
767
768 let args = vec!["ex", "-p"];
769 let config = create_config(args);
770 assert_eq!(true, config.show_precise());
771
772 let args = vec!["ex", "--precise"];
773 let config = create_config(args);
774 assert_eq!(true, config.show_precise());
775 }
776
777 #[test]
778 fn test_utc_is_handled() {
779 let args = vec!["ex"];
780 let config = create_config(args);
781 assert_eq!(false, config.show_utc());
782
783 let args = vec!["ex", "-u"];
784 let config = create_config(args);
785 assert_eq!(true, config.show_utc());
786
787 let args = vec!["ex", "--utc"];
788 let config = create_config(args);
789 assert_eq!(true, config.show_utc());
790 }
791
792 #[test]
793 fn test_total_is_handled() {
794 let args = vec!["ex"];
795 let config = create_config(args);
796 assert_eq!(false, config.show_total());
797
798 let args = vec!["ex", "--total"];
799 let config = create_config(args);
800 assert_eq!(true, config.show_total());
801 }
802
803 #[test]
804 #[cfg(unix)]
805 fn test_owner_is_handled() {
806 let args = vec!["ex"];
807 let config = create_config(args);
808 assert_eq!(false, config.show_owner());
809
810 let args = vec!["ex", "--owner"];
811 let config = create_config(args);
812 assert_eq!(true, config.show_owner());
813 }
814
815 #[test]
816 fn test_data_is_handled() {
817 let args = vec!["ex"];
818 let config = create_config(args);
819 assert_eq!(false, config.show_sig());
820
821 let args = vec!["ex", "--sig"];
822 let config = create_config(args);
823 assert_eq!(true, config.show_sig());
824 }
825
826 #[test]
827 fn test_only_path_is_handled_with_terminal() {
828 let args = vec!["ex"];
829 let config = create_config_with_piped(args, false);
830 assert_eq!(false, config.only_path()); assert_eq!(false, config.escape_path()); assert_eq!(false, config.null_path());
833
834 let args = vec!["ex", "-x"];
835 let config = create_config_with_piped(args, false);
836 assert_eq!(true, config.only_path());
837 assert_eq!(false, config.escape_path());
838 assert_eq!(false, config.null_path());
839
840 let args = vec!["ex", "-x", "-x"];
841 let config = create_config_with_piped(args, false);
842 assert_eq!(false, config.only_path());
843 assert_eq!(false, config.escape_path());
844 assert_eq!(false, config.null_path());
845
846 let args = vec!["ex", "-xx"];
847 let config = create_config_with_piped(args, false);
848 assert_eq!(false, config.only_path());
849 assert_eq!(false, config.escape_path());
850 assert_eq!(false, config.null_path());
851
852 let args = vec!["ex", "--only-path"];
853 let config = create_config_with_piped(args, false);
854 assert_eq!(true, config.only_path());
855 assert_eq!(false, config.escape_path());
856 assert_eq!(false, config.null_path());
857 }
858
859 #[test]
860 fn test_only_path_is_handled_with_piped() {
861 let args = vec!["ex"];
862 let config = create_config_with_piped(args, true);
863 assert_eq!(true, config.only_path()); assert_eq!(true, config.escape_path()); assert_eq!(false, config.null_path());
866
867 let args = vec!["ex", "-x"];
868 let config = create_config_with_piped(args, true);
869 assert_eq!(true, config.only_path());
870 assert_eq!(false, config.escape_path());
871 assert_eq!(false, config.null_path());
872
873 let args = vec!["ex", "-x", "-x"];
874 let config = create_config_with_piped(args, true);
875 assert_eq!(false, config.only_path());
876 assert_eq!(false, config.escape_path());
877 assert_eq!(false, config.null_path());
878
879 let args = vec!["ex", "-xx"];
880 let config = create_config_with_piped(args, true);
881 assert_eq!(false, config.only_path());
882 assert_eq!(false, config.escape_path());
883 assert_eq!(false, config.null_path());
884
885 let args = vec!["ex", "--only-path"];
886 let config = create_config_with_piped(args, true);
887 assert_eq!(true, config.only_path());
888 assert_eq!(false, config.escape_path());
889 assert_eq!(false, config.null_path());
890 }
891
892 #[test]
893 #[cfg(debug_assertions)]
894 fn test_only_path_is_handled_with_override() {
895 let args = vec!["ex", "--terminal"];
896 let config = create_config_with_piped(args, true);
897 assert_eq!(false, config.only_path()); assert_eq!(false, config.escape_path()); assert_eq!(false, config.null_path());
900
901 let args = vec!["ex", "--terminal", "-x"];
902 let config = create_config_with_piped(args, true);
903 assert_eq!(true, config.only_path());
904 assert_eq!(false, config.escape_path());
905 assert_eq!(false, config.null_path());
906
907 let args = vec!["ex", "--terminal", "-x", "-x"];
908 let config = create_config_with_piped(args, true);
909 assert_eq!(false, config.only_path());
910 assert_eq!(false, config.escape_path());
911 assert_eq!(false, config.null_path());
912
913 let args = vec!["ex", "--terminal", "-xx"];
914 let config = create_config_with_piped(args, true);
915 assert_eq!(false, config.only_path());
916 assert_eq!(false, config.escape_path());
917 assert_eq!(false, config.null_path());
918
919 let args = vec!["ex", "--terminal", "--only-path"];
920 let config = create_config_with_piped(args, true);
921 assert_eq!(true, config.only_path());
922 assert_eq!(false, config.escape_path());
923 assert_eq!(false, config.null_path());
924 }
925
926 #[test]
927 fn test_null_path_is_handled() {
928 let args = vec!["ex"];
929 let config = create_config_with_piped(args, false);
930 assert_eq!(false, config.only_path());
931 assert_eq!(false, config.escape_path());
932 assert_eq!(false, config.null_path());
933
934 let args = vec!["ex", "-z"]; let config = create_config_with_piped(args, false);
936 assert_eq!(false, config.only_path());
937 assert_eq!(false, config.escape_path());
938 assert_eq!(false, config.null_path());
939
940 let args = vec!["ex", "--null-path"];
941 let config = create_config_with_piped(args, false);
942 assert_eq!(true, config.only_path());
943 assert_eq!(false, config.escape_path());
944 assert_eq!(true, config.null_path());
945 }
946
947 #[test]
948 fn test_abs_path_is_handled() {
949 let args = vec!["ex"];
950 let config = create_config(args);
951 assert_eq!(false, config.abs_path());
952
953 let args = vec!["ex", "-q"];
954 let config = create_config(args);
955 assert_eq!(true, config.abs_path());
956
957 let args = vec!["ex", "--abs-path"];
958 let config = create_config(args);
959 assert_eq!(true, config.abs_path());
960 }
961
962 #[test]
963 #[cfg(windows)]
964 fn test_win_path_is_handled() {
965 let args = vec!["ex"];
966 let config = create_config(args);
967 assert_eq!(false, config.win_path());
968
969 let args = vec!["ex", "-w"];
970 let config = create_config(args);
971 assert_eq!(true, config.win_path());
972
973 let args = vec!["ex", "--win-path"];
974 let config = create_config(args);
975 assert_eq!(true, config.win_path());
976 }
977
978 #[test]
979 #[cfg(windows)]
980 fn test_win_ver_is_handled() {
981 let args = vec!["ex"];
982 let config = create_config(args);
983 assert_eq!(false, config.win_ver());
984
985 let args = vec!["ex", "-v"];
986 let config = create_config(args);
987 assert_eq!(true, config.win_ver());
988
989 let args = vec!["ex", "--win-ver"];
990 let config = create_config(args);
991 assert_eq!(true, config.win_ver());
992 }
993
994 #[test]
995 fn test_patterns_are_handled() {
996 let expected = vec!["."];
997 let args = vec!["ex"];
998 let config = create_config(args);
999 assert_eq!(&expected, config.patterns());
1000
1001 let expected = vec!["file1"];
1002 let args = vec!["ex", "file1"];
1003 let config = create_config(args);
1004 assert_eq!(&expected, config.patterns());
1005
1006 let expected = vec!["file1", "file2"];
1007 let args = vec!["ex", "file1", "file2"];
1008 let config = create_config(args);
1009 assert_eq!(&expected, config.patterns());
1010 }
1011
1012 #[test]
1013 #[should_panic(expected = "Invalid depth option: foo")]
1014 fn test_unexpected_depth_causes_error() {
1015 let args = vec!["ex", "-dfoo"];
1016 create_config(args);
1017 }
1018
1019 #[test]
1020 #[should_panic(expected = "Invalid order option: foo")]
1021 fn test_unexpected_order_causes_error() {
1022 let args = vec!["ex", "-ofoo"];
1023 create_config(args);
1024 }
1025
1026 #[test]
1027 #[should_panic(expected = "Invalid recent option: foo")]
1028 fn test_unexpected_recent_causes_error() {
1029 let args = vec!["ex", "-rfoo"];
1030 create_config(args);
1031 }
1032
1033 #[test]
1034 #[should_panic(expected = "Invalid type option: z")]
1035 fn test_unexpected_type_causes_error() {
1036 let args = vec!["ex", "-tfzd"];
1037 create_config(args);
1038 }
1039
1040 #[test]
1041 #[should_panic(expected = "Invalid git option: z")]
1042 fn test_unexpected_git_causes_error() {
1043 let args = vec!["ex", "-gazm"];
1044 create_config(args);
1045 }
1046
1047 fn create_config(args: Vec<&str>) -> Config {
1048 create_config_with_piped(args, false)
1049 }
1050
1051 fn create_config_with_piped(args: Vec<&str>, piped: bool) -> Config {
1052 let args = args.into_iter().map(String::from).collect();
1053 Config::new(args, piped, DateTime::default).unwrap()
1054 }
1055
1056 fn create_time(year: i32, month: u32, day: u32, hour: u32, minute: u32, second: u32) -> DateTime<Utc> {
1057 let date = NaiveDate::from_ymd_opt(year, month, day).unwrap();
1058 let time = NaiveTime::from_hms_opt(hour, minute, second).unwrap();
1059 let time = DateTime::from_naive_utc_and_offset(NaiveDateTime::new(date, time), Utc);
1060 time
1061 }
1062
1063 fn create_set<T: Clone + Eq + Hash>(values: &[T]) -> HashSet<T> {
1064 values.iter().map(T::clone).collect()
1065 }
1066
1067 impl Config {
1068 pub fn with_curr_time(
1069 mut self,
1070 year: i32,
1071 month: u32,
1072 day: u32,
1073 hour: u32,
1074 min: u32,
1075 sec: u32,
1076 ) -> Self {
1077 self.curr_time = Utc.with_ymd_and_hms(year, month, day, hour, min, sec).unwrap();
1078 self
1079 }
1080
1081 pub fn with_recurse_all(mut self, recurse_all: bool) -> Self {
1082 self.cli.recurse_all = recurse_all;
1083 self
1084 }
1085
1086 pub fn with_min_depth(mut self, min_depth: usize) -> Self {
1087 self.cli.recurse_depth.get_or_insert_default().min_depth = Some(min_depth);
1088 self
1089 }
1090
1091 pub fn with_max_depth(mut self, max_depth: usize) -> Self {
1092 self.cli.recurse_depth.get_or_insert_default().max_depth = Some(max_depth);
1093 self
1094 }
1095
1096 pub fn with_show_indent(mut self, show_indent: bool) -> Self {
1097 self.cli.show_indent = show_indent;
1098 self
1099 }
1100
1101 pub fn with_zip_expand(mut self, zip_expand: bool) -> Self {
1102 self.cli.zip_expand = zip_expand;
1103 self
1104 }
1105
1106 pub fn with_case_sensitive(mut self, case_sensitive: bool) -> Self {
1107 if case_sensitive {
1108 self.cli.case_sensitive = true;
1109 self.cli.case_insensitive = false;
1110 } else {
1111 self.cli.case_sensitive = false;
1112 self.cli.case_insensitive = true;
1113 }
1114 self
1115 }
1116
1117 pub fn with_sort_order(mut self, sort_order: Vec<OrderKind>) -> Self {
1118 self.sort_order = sort_order;
1119 self
1120 }
1121
1122 pub fn with_sort_name(mut self, sort_name: bool) -> Self {
1123 self.sort_name = sort_name;
1124 self
1125 }
1126
1127 pub fn with_filter_recent(mut self, filter_recent: RecentKind) -> Self {
1128 self.cli.filter_recent = Some(filter_recent);
1129 self
1130 }
1131
1132 pub fn with_filter_types(mut self, filter_types: Vec<FileKind>) -> Self {
1133 let filter_types = FileSet { inner: filter_types.into_iter().collect() };
1134 self.cli.filter_types = Some(filter_types);
1135 self
1136 }
1137
1138 pub fn with_filter_git(mut self, filter_git: GitFlags) -> Self {
1139 self.cli.filter_git = Some(filter_git);
1140 self
1141 }
1142
1143 pub fn with_show_precise(mut self, show_precise: bool) -> Self {
1144 self.cli.show_precise = show_precise;
1145 self
1146 }
1147
1148 pub fn with_show_utc(mut self, show_utc: bool) -> Self {
1149 self.cli.show_utc = show_utc;
1150 self
1151 }
1152
1153 pub fn with_show_total(mut self, show_total: bool) -> Self {
1154 self.cli.show_total = show_total;
1155 self
1156 }
1157
1158 #[cfg(unix)]
1159 pub fn with_show_owner(mut self, show_owner: bool) -> Self {
1160 self.cli.show_owner = show_owner;
1161 self
1162 }
1163
1164 pub fn with_show_crc(mut self, show_crc: bool) -> Self {
1165 self.cli.show_crc = show_crc;
1166 self
1167 }
1168
1169 pub fn with_show_sig(mut self, show_sig: bool) -> Self {
1170 self.cli.show_sig = show_sig;
1171 self
1172 }
1173
1174 pub fn with_only_path(mut self, only_path: bool) -> Self {
1175 self.cli.only_path = if only_path { 1 } else { 2 };
1176 self
1177 }
1178
1179 pub fn with_escape_path(mut self) -> Self {
1180 self.cli.only_path = 0;
1181 self.cli.null_path = false;
1182 self.piped = true;
1183 self
1184 }
1185
1186 pub fn with_abs_path(mut self, abs_path: bool) -> Self {
1187 self.cli.abs_path = abs_path;
1188 self
1189 }
1190
1191 #[cfg(windows)]
1192 pub fn with_win_ver(mut self, win_ver: bool) -> Self {
1193 self.cli.win_ver = win_ver;
1194 self
1195 }
1196
1197 pub fn with_patterns(mut self, patterns: Vec<&str>) -> Self {
1198 self.cli.patterns = patterns.into_iter().map(String::from).collect();
1199 self
1200 }
1201 }
1202}