Skip to main content

ex_cli/
config.rs

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