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()); assert_eq!(false, config.escape_path()); 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()); assert_eq!(true, config.escape_path()); 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()); assert_eq!(false, config.escape_path()); 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"]; 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}