1use crate::cli::order::{DirKind, OrderKind};
2use crate::config::Config;
3use crate::fs::file::File;
4use itertools::{EitherOrBoth, Itertools, ZipLongest};
5use std::cmp::Ordering;
6use std::path::Components;
7
8pub struct Sorter<'a> {
9 config: &'a Config,
10}
11
12impl<'a> Sorter<'a> {
13 pub fn new(config: &'a Config) -> Self {
14 Self { config }
15 }
16
17 pub fn sort_files(&self, files: &mut Vec<File>) {
18 files.sort_unstable_by(|x, y| self.cmp_files(x, y));
19 }
20
21 fn cmp_files(&self, left: &File, right: &File) -> Ordering {
22 for order in self.config.sort_order() {
23 let result = match order {
24 OrderKind::Dir => Self::cmp_dirs(left, right),
25 OrderKind::Group => Self::cmp_groups(left, right),
26 OrderKind::Name => Self::cmp_names(left, right),
27 OrderKind::Ext => Self::cmp_exts(left, right),
28 OrderKind::Size(dir) => Self::cmp_sizes(left, right, dir),
29 OrderKind::Time(dir) => Self::cmp_times(left, right, dir),
30 };
31 if result != Ordering::Equal {
32 return result;
33 }
34 }
35 Ordering::Equal
36 }
37
38 fn cmp_dirs(left: &File, right: &File) -> Ordering {
39 for pair in Self::zip_dirs(left, right) {
40 let result = match pair {
41 EitherOrBoth::Both(left, right) => {
42 let left = left.as_os_str().to_str().unwrap_or_default();
43 let right = right.as_os_str().to_str().unwrap_or_default();
44 Self::cmp_strings(left, right)
45 }
46 EitherOrBoth::Left(_) => Ordering::Greater,
47 EitherOrBoth::Right(_) => Ordering::Less,
48 };
49 if result != Ordering::Equal {
50 return result;
51 }
52 }
53 Self::cmp_groups(left, right)
54 }
55
56 fn zip_dirs<'b>(left: &'b File, right: &'b File) -> ZipLongest<Components<'b>, Components<'b>> {
57 let left = left.abs_dir.components();
58 let right = right.abs_dir.components();
59 left.zip_longest(right)
60 }
61
62 fn cmp_groups(left: &File, right: &File) -> Ordering {
63 let left = left.group_dir_before_file();
64 let right = right.group_dir_before_file();
65 left.cmp(&right)
66 }
67
68 fn cmp_names(left: &File, right: &File) -> Ordering {
69 Self::cmp_strings(&left.file_name, &right.file_name)
70 }
71
72 fn cmp_exts(left: &File, right: &File) -> Ordering {
73 Self::cmp_strings(&left.file_ext, &right.file_ext)
74 }
75
76 fn cmp_strings(left: &str, right: &str) -> Ordering {
77 let result = natord::compare_ignore_case(left, right);
78 if result != Ordering::Equal {
79 return result;
80 }
81 let result = natord::compare(left, right);
82 if result != Ordering::Equal {
83 return result;
84 }
85 Ordering::Equal
86 }
87
88 fn cmp_sizes(left: &File, right: &File, dir: &DirKind) -> Ordering {
89 let result = left.file_size.cmp(&right.file_size);
90 match dir {
91 DirKind::Asc => result,
92 DirKind::Desc => result.reverse(),
93 }
94 }
95
96 fn cmp_times(left: &File, right: &File, dir: &DirKind) -> Ordering {
97 let result = left.file_time.cmp(&right.file_time);
98 match dir {
99 DirKind::Asc => result,
100 DirKind::Desc => result.reverse(),
101 }
102 }
103}
104
105#[cfg(test)]
107mod tests {
108 use crate::cli::file::FileKind;
109 use crate::cli::order::{DirKind, OrderKind};
110 use crate::config::Config;
111 use crate::fs::file::File;
112 use crate::sorter::Sorter;
113 use chrono::{DateTime, Utc};
114 use pretty_assertions::assert_eq;
115 use std::ffi::OsStr;
116 use std::path::{PathBuf, MAIN_SEPARATOR_STR};
117
118 #[test]
119 fn test_files_are_sorted_by_path() {
120 let expected = string_from(vec![
121 "index",
122 "cheese/",
123 "cheese/index",
124 "cheese/english/",
125 "cheese/english/cheddar.xy",
126 "cheese/english/stilton.ij",
127 "cheese/french/",
128 "cheese/french/bleu.xy",
129 "cheese/french/brie.ij",
130 "fruit/",
131 "fruit/citrus/",
132 "fruit/citrus/lemon.xy",
133 "fruit/citrus/orange.ij",
134 "fruit/other/",
135 "fruit/other/apple.xy",
136 "fruit/other/banana.ij",
137 "fruit/other/cherry.xy",
138 "fruit/other/date.ij",
139 "fruit.zip/",
140 "fruit.zip/citrus/",
141 "fruit.zip/citrus/lemon.xy",
142 "fruit.zip/citrus/orange.ij",
143 "fruit.zip/other/",
144 "fruit.zip/other/apple.xy",
145 "fruit.zip/other/banana.ij",
146 "fruit.zip/other/cherry.xy",
147 "fruit.zip/other/date.ij",
148 ]);
149 let files = create_files();
150 let paths = sort_files(files, vec![OrderKind::Dir, OrderKind::Name]);
151 assert_eq!(expected, paths);
152 }
153
154 #[test]
155 fn test_files_are_sorted_by_dir_and_size() {
156 let expected = string_from(vec![
157 "index", "cheese/", "cheese/index", "cheese/english/", "cheese/english/stilton.ij", "cheese/english/cheddar.xy", "cheese/french/", "cheese/french/brie.ij", "cheese/french/bleu.xy", "fruit/", "fruit/citrus/", "fruit/citrus/lemon.xy", "fruit/citrus/orange.ij", "fruit/other/", "fruit/other/date.ij", "fruit/other/banana.ij", "fruit/other/apple.xy", "fruit/other/cherry.xy", "fruit.zip/", "fruit.zip/citrus/", "fruit.zip/citrus/lemon.xy", "fruit.zip/citrus/orange.ij", "fruit.zip/other/", "fruit.zip/other/date.ij", "fruit.zip/other/banana.ij", "fruit.zip/other/apple.xy", "fruit.zip/other/cherry.xy", ]);
185 let files = create_files();
186 let paths = sort_files(files, vec![OrderKind::Dir, OrderKind::Size(DirKind::Asc), OrderKind::Name]);
187 assert_eq!(expected, paths);
188 }
189
190 #[test]
191 fn test_files_are_sorted_by_dir_and_time() {
192 let expected = string_from(vec![
193 "index", "cheese/", "cheese/index", "cheese/english/", "cheese/english/cheddar.xy", "cheese/english/stilton.ij", "cheese/french/", "cheese/french/bleu.xy", "cheese/french/brie.ij", "fruit/", "fruit/citrus/", "fruit/citrus/orange.ij", "fruit/citrus/lemon.xy", "fruit/other/", "fruit/other/date.ij", "fruit/other/cherry.xy", "fruit/other/apple.xy", "fruit/other/banana.ij", "fruit.zip/", "fruit.zip/citrus/", "fruit.zip/citrus/orange.ij", "fruit.zip/citrus/lemon.xy", "fruit.zip/other/", "fruit.zip/other/date.ij", "fruit.zip/other/cherry.xy", "fruit.zip/other/apple.xy", "fruit.zip/other/banana.ij", ]);
221 let files = create_files();
222 let paths = sort_files(files, vec![OrderKind::Dir, OrderKind::Time(DirKind::Asc), OrderKind::Name]);
223 assert_eq!(expected, paths);
224 }
225
226 #[test]
227 fn test_files_are_sorted_by_name() {
228 let expected = string_from(vec![
229 "cheese/", "cheese/english/", "cheese/french/", "fruit/", "fruit/citrus/", "fruit/other/", "fruit.zip/", "fruit.zip/citrus/", "fruit.zip/other/", "fruit/other/apple.xy", "fruit.zip/other/apple.xy", "fruit/other/banana.ij", "fruit.zip/other/banana.ij", "cheese/french/bleu.xy", "cheese/french/brie.ij", "cheese/english/cheddar.xy", "fruit/other/cherry.xy", "fruit.zip/other/cherry.xy", "fruit/other/date.ij", "fruit.zip/other/date.ij", "index", "cheese/index", "fruit/citrus/lemon.xy", "fruit.zip/citrus/lemon.xy", "fruit/citrus/orange.ij", "fruit.zip/citrus/orange.ij", "cheese/english/stilton.ij", ]);
257 let files = create_files();
258 let paths = sort_files(files, vec![OrderKind::Name, OrderKind::Dir]);
259 assert_eq!(expected, paths);
260 }
261
262 #[test]
263 fn test_files_are_sorted_by_ext() {
264 let expected = string_from(vec![
265 "index", "cheese/", "cheese/index", "cheese/english/", "cheese/french/", "fruit/", "fruit/citrus/", "fruit/other/", "fruit.zip/", "fruit.zip/citrus/", "fruit.zip/other/", "cheese/english/stilton.ij", "cheese/french/brie.ij", "fruit/citrus/orange.ij", "fruit/other/banana.ij", "fruit/other/date.ij", "fruit.zip/citrus/orange.ij", "fruit.zip/other/banana.ij", "fruit.zip/other/date.ij", "cheese/english/cheddar.xy", "cheese/french/bleu.xy", "fruit/citrus/lemon.xy", "fruit/other/apple.xy", "fruit/other/cherry.xy", "fruit.zip/citrus/lemon.xy", "fruit.zip/other/apple.xy", "fruit.zip/other/cherry.xy", ]);
293 let files = create_files();
294 let paths = sort_files(files, vec![OrderKind::Ext, OrderKind::Dir, OrderKind::Name]);
295 assert_eq!(expected, paths);
296 }
297
298 #[test]
299 fn test_files_are_sorted_by_ext_and_size() {
300 let expected = string_from(vec![
301 "cheese/", "cheese/english/", "cheese/french/", "fruit/", "fruit/citrus/", "fruit/other/", "fruit.zip/", "fruit.zip/citrus/", "fruit.zip/other/", "index", "cheese/index", "fruit/other/date.ij", "fruit.zip/other/date.ij", "fruit/other/banana.ij", "fruit.zip/other/banana.ij", "cheese/english/stilton.ij", "fruit/citrus/orange.ij", "fruit.zip/citrus/orange.ij", "cheese/french/brie.ij", "fruit/citrus/lemon.xy", "fruit.zip/citrus/lemon.xy", "fruit/other/apple.xy", "fruit.zip/other/apple.xy", "fruit/other/cherry.xy", "fruit.zip/other/cherry.xy", "cheese/english/cheddar.xy", "cheese/french/bleu.xy", ]);
329 let files = create_files();
330 let paths = sort_files(files, vec![OrderKind::Ext, OrderKind::Size(DirKind::Asc), OrderKind::Dir, OrderKind::Name]);
331 assert_eq!(expected, paths);
332 }
333
334 #[test]
335 fn test_files_are_sorted_by_size_asc() {
336 let expected = string_from(vec![
337 "cheese/", "cheese/english/", "cheese/french/", "fruit/", "fruit/citrus/", "fruit/other/", "fruit.zip/", "fruit.zip/citrus/", "fruit.zip/other/", "fruit/other/date.ij", "fruit.zip/other/date.ij", "index", "fruit/other/banana.ij", "fruit.zip/other/banana.ij", "cheese/english/stilton.ij", "cheese/index", "fruit/citrus/lemon.xy", "fruit.zip/citrus/lemon.xy", "fruit/citrus/orange.ij", "fruit.zip/citrus/orange.ij", "fruit/other/apple.xy", "fruit.zip/other/apple.xy", "fruit/other/cherry.xy", "fruit.zip/other/cherry.xy", "cheese/english/cheddar.xy", "cheese/french/brie.ij", "cheese/french/bleu.xy", ]);
365 let files = create_files();
366 let paths = sort_files(files, vec![OrderKind::Size(DirKind::Asc), OrderKind::Dir, OrderKind::Name]);
367 assert_eq!(expected, paths);
368 }
369
370 #[test]
371 fn test_files_are_sorted_by_size_desc() {
372 let expected = string_from(vec![
373 "cheese/french/bleu.xy", "cheese/french/brie.ij", "cheese/english/cheddar.xy", "fruit/other/cherry.xy", "fruit.zip/other/cherry.xy", "fruit/other/apple.xy", "fruit.zip/other/apple.xy", "fruit/citrus/orange.ij", "fruit.zip/citrus/orange.ij", "fruit/citrus/lemon.xy", "fruit.zip/citrus/lemon.xy", "cheese/index", "cheese/english/stilton.ij", "fruit/other/banana.ij", "fruit.zip/other/banana.ij", "index", "fruit/other/date.ij", "fruit.zip/other/date.ij", "cheese/", "cheese/english/", "cheese/french/", "fruit/", "fruit/citrus/", "fruit/other/", "fruit.zip/", "fruit.zip/citrus/", "fruit.zip/other/", ]);
401 let files = create_files();
402 let paths = sort_files(files, vec![OrderKind::Size(DirKind::Desc), OrderKind::Dir, OrderKind::Name]);
403 assert_eq!(expected, paths);
404 }
405
406 #[test]
407 fn test_files_are_sorted_by_time_asc() {
408 let expected = string_from(vec![
409 "cheese/english/cheddar.xy", "cheese/french/", "cheese/french/bleu.xy", "fruit/other/date.ij", "fruit.zip/other/date.ij", "fruit/other/cherry.xy", "fruit.zip/other/cherry.xy", "cheese/index", "cheese/", "cheese/french/brie.ij", "fruit/citrus/orange.ij", "fruit.zip/citrus/orange.ij", "cheese/english/stilton.ij", "fruit/", "fruit.zip/", "fruit/other/apple.xy", "fruit.zip/other/apple.xy", "fruit/other/banana.ij", "fruit.zip/other/banana.ij", "fruit/citrus/lemon.xy", "fruit.zip/citrus/lemon.xy", "index", "fruit/citrus/", "fruit.zip/citrus/", "cheese/english/", "fruit/other/", "fruit.zip/other/", ]);
437 let files = create_files();
438 let paths = sort_files(files, vec![OrderKind::Time(DirKind::Asc), OrderKind::Dir, OrderKind::Name]);
439 assert_eq!(expected, paths);
440 }
441
442 #[test]
443 fn test_files_are_sorted_by_time_desc() {
444 let expected = string_from(vec![
445 "fruit/other/", "fruit.zip/other/", "cheese/english/", "fruit/citrus/", "fruit.zip/citrus/", "index", "fruit/citrus/lemon.xy", "fruit.zip/citrus/lemon.xy", "fruit/other/banana.ij", "fruit.zip/other/banana.ij", "fruit/other/apple.xy", "fruit.zip/other/apple.xy", "fruit/", "fruit.zip/", "cheese/english/stilton.ij", "fruit/citrus/orange.ij", "fruit.zip/citrus/orange.ij", "cheese/french/brie.ij", "cheese/", "cheese/index", "fruit/other/cherry.xy", "fruit.zip/other/cherry.xy", "fruit/other/date.ij", "fruit.zip/other/date.ij", "cheese/french/bleu.xy", "cheese/french/", "cheese/english/cheddar.xy", ]);
473 let files = create_files();
474 let paths = sort_files(files, vec![OrderKind::Time(DirKind::Desc), OrderKind::Dir, OrderKind::Name]);
475 assert_eq!(expected, paths);
476 }
477
478 #[test]
479 fn test_files_are_sorted_by_group() {
480 let expected = string_from(vec![
481 "cheese/", "cheese/english/", "cheese/french/", "fruit/", "fruit/citrus/", "fruit/other/", "fruit.zip/", "fruit.zip/citrus/", "fruit.zip/other/", "index", "cheese/index", "cheese/english/cheddar.xy", "cheese/english/stilton.ij", "cheese/french/bleu.xy", "cheese/french/brie.ij", "fruit/citrus/lemon.xy", "fruit/citrus/orange.ij", "fruit/other/apple.xy", "fruit/other/banana.ij", "fruit/other/cherry.xy", "fruit/other/date.ij", "fruit.zip/citrus/lemon.xy", "fruit.zip/citrus/orange.ij", "fruit.zip/other/apple.xy", "fruit.zip/other/banana.ij", "fruit.zip/other/cherry.xy", "fruit.zip/other/date.ij", ]);
509 let files = create_files();
510 let paths = sort_files(files, vec![OrderKind::Group, OrderKind::Dir, OrderKind::Name]);
511 assert_eq!(expected, paths);
512 }
513
514 #[test]
515 fn test_files_are_sorted_by_case_insensitive_path() {
516 let expected = string_from(vec![
517 "AAA.DD",
518 "AAA.dd",
519 "aaa.DD",
520 "aaa.dd",
521 "AAA.EE",
522 "AAA.ee",
523 "aaa.EE",
524 "aaa.ee",
525 "AAA.FF",
526 "AAA.ff",
527 "aaa.FF",
528 "aaa.ff",
529 "BBB.DD",
530 "BBB.dd",
531 "bbb.DD",
532 "bbb.dd",
533 "BBB.EE",
534 "BBB.ee",
535 "bbb.EE",
536 "bbb.ee",
537 "BBB.FF",
538 "BBB.ff",
539 "bbb.FF",
540 "bbb.ff",
541 "CCC.DD",
542 "CCC.dd",
543 "ccc.DD",
544 "ccc.dd",
545 "CCC.EE",
546 "CCC.ee",
547 "ccc.EE",
548 "ccc.ee",
549 "CCC.FF",
550 "CCC.ff",
551 "ccc.FF",
552 "ccc.ff",
553 ]);
554 let files = create_case();
555 let paths = sort_files(files, vec![OrderKind::Dir, OrderKind::Name]);
556 assert_eq!(expected, paths);
557 }
558
559 #[test]
560 fn test_files_are_sorted_by_case_insensitive_ext() {
561 let expected = string_from(vec![
562 "AAA.DD",
563 "aaa.DD",
564 "BBB.DD",
565 "bbb.DD",
566 "CCC.DD",
567 "ccc.DD",
568 "AAA.dd",
569 "aaa.dd",
570 "BBB.dd",
571 "bbb.dd",
572 "CCC.dd",
573 "ccc.dd",
574 "AAA.EE",
575 "aaa.EE",
576 "BBB.EE",
577 "bbb.EE",
578 "CCC.EE",
579 "ccc.EE",
580 "AAA.ee",
581 "aaa.ee",
582 "BBB.ee",
583 "bbb.ee",
584 "CCC.ee",
585 "ccc.ee",
586 "AAA.FF",
587 "aaa.FF",
588 "BBB.FF",
589 "bbb.FF",
590 "CCC.FF",
591 "ccc.FF",
592 "AAA.ff",
593 "aaa.ff",
594 "BBB.ff",
595 "bbb.ff",
596 "CCC.ff",
597 "ccc.ff",
598 ]);
599 let files = create_case();
600 let paths = sort_files(files, vec![OrderKind::Ext, OrderKind::Dir, OrderKind::Name]);
601 assert_eq!(expected, paths);
602 }
603
604 #[test]
605 fn test_files_are_sorted_before_dirs() {
606 let expected = string_from(vec![
607 "xyz",
608 "abc/",
609 "abc/xyz",
610 "abc/def/",
611 "abc/def/xyz",
612 ]);
613 let files = create_folder();
614 let paths = sort_files(files, vec![OrderKind::Dir, OrderKind::Name]);
615 assert_eq!(expected, paths);
616 }
617
618 #[test]
619 fn test_numeric_files_are_sorted_by_dir() {
620 let expected = string_from(vec![
621 "9/",
622 "9/9/",
623 "9/9/9/",
624 "9/9/10/",
625 "9/10/",
626 "9/10/9/",
627 "9/10/10/",
628 "10/",
629 "10/9/",
630 "10/9/9/",
631 "10/9/10/",
632 "10/10/",
633 "10/10/9/",
634 "10/10/10/",
635 ]);
636 let files = create_numeric1();
637 let paths = sort_files(files, vec![OrderKind::Dir, OrderKind::Name]);
638 assert_eq!(expected, paths);
639 }
640
641 #[test]
642 fn test_numeric_files_are_sorted_by_name() {
643 let expected = string_from(vec![
644 "9",
645 "9_9",
646 "9_9_9",
647 "9_9_10",
648 "9_10",
649 "9_10_9",
650 "9_10_10",
651 "10",
652 "10_9",
653 "10_9_9",
654 "10_9_10",
655 "10_10",
656 "10_10_9",
657 "10_10_10",
658 ]);
659 let files = create_numeric2();
660 let paths = sort_files(files, vec![OrderKind::Dir, OrderKind::Name]);
661 assert_eq!(expected, paths);
662 }
663
664 #[test]
665 fn test_numeric_files_are_sorted_by_ext() {
666 let expected = string_from(vec![
667 "file.9",
668 "file.9_9",
669 "file.9_9_9",
670 "file.9_9_10",
671 "file.9_10",
672 "file.9_10_9",
673 "file.9_10_10",
674 "file.10",
675 "file.10_9",
676 "file.10_9_9",
677 "file.10_9_10",
678 "file.10_10",
679 "file.10_10_9",
680 "file.10_10_10",
681 ]);
682 let files = create_numeric3();
683 let paths = sort_files(files, vec![OrderKind::Ext, OrderKind::Dir, OrderKind::Name]);
684 assert_eq!(expected, paths);
685 }
686
687 fn create_files() -> Vec<File> {
688 vec![
689 create_file(FileKind::Other, "2023-07-05T13:05:42Z", 42, "index"),
690 create_file(FileKind::Dir, "2023-01-16T11:25:51Z", 0, "cheese"),
691 create_file(FileKind::Other, "2022-12-23T21:43:18Z", 99, "cheese/index"),
692 create_file(FileKind::Dir, "2023-08-04T10:57:26Z", 0, "cheese/english"),
693 create_file(FileKind::Other, "2022-09-10T04:03:38Z", 6363, "cheese/english/cheddar.xy"),
694 create_file(FileKind::Other, "2023-04-07T03:08:29Z", 91, "cheese/english/stilton.ij"),
695 create_file(FileKind::Dir, "2022-10-06T23:36:27Z", 0, "cheese/french"),
696 create_file(FileKind::Other, "2022-10-28T05:49:07Z", 20122, "cheese/french/bleu.xy"),
697 create_file(FileKind::Other, "2023-02-28T11:01:59Z", 8681, "cheese/french/brie.ij"),
698 create_file(FileKind::Dir, "2023-04-18T17:09:31Z", 0, "fruit"),
699 create_file(FileKind::Dir, "2023-07-13T08:58:00Z", 0, "fruit/citrus"),
700 create_file(FileKind::Other, "2023-06-29T21:16:08Z", 108, "fruit/citrus/lemon.xy"),
701 create_file(FileKind::Other, "2023-03-27T05:45:33Z", 173, "fruit/citrus/orange.ij"),
702 create_file(FileKind::Dir, "2023-08-14T01:53:00Z", 0, "fruit/other"),
703 create_file(FileKind::Other, "2023-05-10T11:24:22Z", 467, "fruit/other/apple.xy"),
704 create_file(FileKind::Other, "2023-06-10T02:06:38Z", 73, "fruit/other/banana.ij"),
705 create_file(FileKind::Other, "2022-12-19T02:12:38Z", 795, "fruit/other/cherry.xy"),
706 create_file(FileKind::Other, "2022-11-25T14:42:53Z", 6, "fruit/other/date.ij"),
707 create_file(FileKind::Dir, "2023-04-18T17:09:31Z", 0, "fruit.zip"),
708 create_file(FileKind::Dir, "2023-07-13T08:58:00Z", 0, "fruit.zip/citrus"),
709 create_file(FileKind::Other, "2023-06-29T21:16:08Z", 108, "fruit.zip/citrus/lemon.xy"),
710 create_file(FileKind::Other, "2023-03-27T05:45:33Z", 173, "fruit.zip/citrus/orange.ij"),
711 create_file(FileKind::Dir, "2023-08-14T01:53:00Z", 0, "fruit.zip/other"),
712 create_file(FileKind::Other, "2023-05-10T11:24:22Z", 467, "fruit.zip/other/apple.xy"),
713 create_file(FileKind::Other, "2023-06-10T02:06:38Z", 73, "fruit.zip/other/banana.ij"),
714 create_file(FileKind::Other, "2022-12-19T02:12:38Z", 795, "fruit.zip/other/cherry.xy"),
715 create_file(FileKind::Other, "2022-11-25T14:42:53Z", 6, "fruit.zip/other/date.ij"),
716 ]
717 }
718
719 fn create_case() -> Vec<File> {
720 vec![
721 create_file(FileKind::Other, "1970-01-01T00:00:00Z", 0, "aaa.dd"),
722 create_file(FileKind::Other, "1970-01-01T00:00:00Z", 0, "aaa.DD"),
723 create_file(FileKind::Other, "1970-01-01T00:00:00Z", 0, "aaa.ee"),
724 create_file(FileKind::Other, "1970-01-01T00:00:00Z", 0, "aaa.EE"),
725 create_file(FileKind::Other, "1970-01-01T00:00:00Z", 0, "aaa.ff"),
726 create_file(FileKind::Other, "1970-01-01T00:00:00Z", 0, "aaa.FF"),
727 create_file(FileKind::Other, "1970-01-01T00:00:00Z", 0, "AAA.dd"),
728 create_file(FileKind::Other, "1970-01-01T00:00:00Z", 0, "AAA.DD"),
729 create_file(FileKind::Other, "1970-01-01T00:00:00Z", 0, "AAA.ee"),
730 create_file(FileKind::Other, "1970-01-01T00:00:00Z", 0, "AAA.EE"),
731 create_file(FileKind::Other, "1970-01-01T00:00:00Z", 0, "AAA.ff"),
732 create_file(FileKind::Other, "1970-01-01T00:00:00Z", 0, "AAA.FF"),
733 create_file(FileKind::Other, "1970-01-01T00:00:00Z", 0, "bbb.dd"),
734 create_file(FileKind::Other, "1970-01-01T00:00:00Z", 0, "bbb.DD"),
735 create_file(FileKind::Other, "1970-01-01T00:00:00Z", 0, "bbb.ee"),
736 create_file(FileKind::Other, "1970-01-01T00:00:00Z", 0, "bbb.EE"),
737 create_file(FileKind::Other, "1970-01-01T00:00:00Z", 0, "bbb.ff"),
738 create_file(FileKind::Other, "1970-01-01T00:00:00Z", 0, "bbb.FF"),
739 create_file(FileKind::Other, "1970-01-01T00:00:00Z", 0, "BBB.dd"),
740 create_file(FileKind::Other, "1970-01-01T00:00:00Z", 0, "BBB.DD"),
741 create_file(FileKind::Other, "1970-01-01T00:00:00Z", 0, "BBB.ee"),
742 create_file(FileKind::Other, "1970-01-01T00:00:00Z", 0, "BBB.EE"),
743 create_file(FileKind::Other, "1970-01-01T00:00:00Z", 0, "BBB.ff"),
744 create_file(FileKind::Other, "1970-01-01T00:00:00Z", 0, "BBB.FF"),
745 create_file(FileKind::Other, "1970-01-01T00:00:00Z", 0, "ccc.dd"),
746 create_file(FileKind::Other, "1970-01-01T00:00:00Z", 0, "ccc.DD"),
747 create_file(FileKind::Other, "1970-01-01T00:00:00Z", 0, "ccc.ee"),
748 create_file(FileKind::Other, "1970-01-01T00:00:00Z", 0, "ccc.EE"),
749 create_file(FileKind::Other, "1970-01-01T00:00:00Z", 0, "ccc.ff"),
750 create_file(FileKind::Other, "1970-01-01T00:00:00Z", 0, "ccc.FF"),
751 create_file(FileKind::Other, "1970-01-01T00:00:00Z", 0, "CCC.dd"),
752 create_file(FileKind::Other, "1970-01-01T00:00:00Z", 0, "CCC.DD"),
753 create_file(FileKind::Other, "1970-01-01T00:00:00Z", 0, "CCC.ee"),
754 create_file(FileKind::Other, "1970-01-01T00:00:00Z", 0, "CCC.EE"),
755 create_file(FileKind::Other, "1970-01-01T00:00:00Z", 0, "CCC.ff"),
756 create_file(FileKind::Other, "1970-01-01T00:00:00Z", 0, "CCC.FF"),
757 ]
758 }
759
760 fn create_folder() -> Vec<File> {
761 vec![
762 create_file(FileKind::Dir, "1970-01-01T00:00:00Z", 0, "abc"),
763 create_file(FileKind::Dir, "1970-01-01T00:00:00Z", 0, "abc/def"),
764 create_file(FileKind::Other, "1970-01-01T00:00:00Z", 0, "abc/def/xyz"),
765 create_file(FileKind::Other, "1970-01-01T00:00:00Z", 0, "abc/xyz"),
766 create_file(FileKind::Other, "1970-01-01T00:00:00Z", 0, "xyz"),
767 ]
768 }
769
770 fn create_numeric1() -> Vec<File> {
771 vec![
772 create_file(FileKind::Dir, "1970-01-01T00:00:00Z", 0, "10"),
773 create_file(FileKind::Dir, "1970-01-01T00:00:00Z", 0, "10/10"),
774 create_file(FileKind::Dir, "1970-01-01T00:00:00Z", 0, "10/10/10"),
775 create_file(FileKind::Dir, "1970-01-01T00:00:00Z", 0, "10/10/9"),
776 create_file(FileKind::Dir, "1970-01-01T00:00:00Z", 0, "10/9"),
777 create_file(FileKind::Dir, "1970-01-01T00:00:00Z", 0, "10/9/10"),
778 create_file(FileKind::Dir, "1970-01-01T00:00:00Z", 0, "10/9/9"),
779 create_file(FileKind::Dir, "1970-01-01T00:00:00Z", 0, "9"),
780 create_file(FileKind::Dir, "1970-01-01T00:00:00Z", 0, "9/10"),
781 create_file(FileKind::Dir, "1970-01-01T00:00:00Z", 0, "9/10/10"),
782 create_file(FileKind::Dir, "1970-01-01T00:00:00Z", 0, "9/10/9"),
783 create_file(FileKind::Dir, "1970-01-01T00:00:00Z", 0, "9/9"),
784 create_file(FileKind::Dir, "1970-01-01T00:00:00Z", 0, "9/9/10"),
785 create_file(FileKind::Dir, "1970-01-01T00:00:00Z", 0, "9/9/9"),
786 ]
787 }
788
789 fn create_numeric2() -> Vec<File> {
790 vec![
791 create_file(FileKind::Other, "1970-01-01T00:00:00Z", 0, "10"),
792 create_file(FileKind::Other, "1970-01-01T00:00:00Z", 0, "10_10"),
793 create_file(FileKind::Other, "1970-01-01T00:00:00Z", 0, "10_10_10"),
794 create_file(FileKind::Other, "1970-01-01T00:00:00Z", 0, "10_10_9"),
795 create_file(FileKind::Other, "1970-01-01T00:00:00Z", 0, "10_9"),
796 create_file(FileKind::Other, "1970-01-01T00:00:00Z", 0, "10_9_10"),
797 create_file(FileKind::Other, "1970-01-01T00:00:00Z", 0, "10_9_9"),
798 create_file(FileKind::Other, "1970-01-01T00:00:00Z", 0, "9"),
799 create_file(FileKind::Other, "1970-01-01T00:00:00Z", 0, "9_10"),
800 create_file(FileKind::Other, "1970-01-01T00:00:00Z", 0, "9_10_10"),
801 create_file(FileKind::Other, "1970-01-01T00:00:00Z", 0, "9_10_9"),
802 create_file(FileKind::Other, "1970-01-01T00:00:00Z", 0, "9_9"),
803 create_file(FileKind::Other, "1970-01-01T00:00:00Z", 0, "9_9_10"),
804 create_file(FileKind::Other, "1970-01-01T00:00:00Z", 0, "9_9_9"),
805 ]
806 }
807
808 fn create_numeric3() -> Vec<File> {
809 vec![
810 create_file(FileKind::Other, "1970-01-01T00:00:00Z", 0, "file.10"),
811 create_file(FileKind::Other, "1970-01-01T00:00:00Z", 0, "file.10_10"),
812 create_file(FileKind::Other, "1970-01-01T00:00:00Z", 0, "file.10_10_10"),
813 create_file(FileKind::Other, "1970-01-01T00:00:00Z", 0, "file.10_10_9"),
814 create_file(FileKind::Other, "1970-01-01T00:00:00Z", 0, "file.10_9"),
815 create_file(FileKind::Other, "1970-01-01T00:00:00Z", 0, "file.10_9_10"),
816 create_file(FileKind::Other, "1970-01-01T00:00:00Z", 0, "file.10_9_9"),
817 create_file(FileKind::Other, "1970-01-01T00:00:00Z", 0, "file.9"),
818 create_file(FileKind::Other, "1970-01-01T00:00:00Z", 0, "file.9_10"),
819 create_file(FileKind::Other, "1970-01-01T00:00:00Z", 0, "file.9_10_10"),
820 create_file(FileKind::Other, "1970-01-01T00:00:00Z", 0, "file.9_10_9"),
821 create_file(FileKind::Other, "1970-01-01T00:00:00Z", 0, "file.9_9"),
822 create_file(FileKind::Other, "1970-01-01T00:00:00Z", 0, "file.9_9_10"),
823 create_file(FileKind::Other, "1970-01-01T00:00:00Z", 0, "file.9_9_9"),
824 ]
825 }
826
827 fn create_file(
828 file_type: FileKind,
829 file_time: &str,
830 file_size: u64,
831 rel_path: &str,
832 ) -> File {
833 let abs_path = PathBuf::from("/root/").join(rel_path);
834 let rel_path = PathBuf::from(rel_path);
835 let file_depth = rel_path.components().count();
836 let file_time = DateTime::parse_from_rfc3339(file_time).unwrap().with_timezone(&Utc);
837 if file_type == FileKind::Dir {
838 let file_name = String::from("");
839 let file_ext = String::from("");
840 File::new(abs_path, rel_path, file_depth, None, file_name, file_ext, file_type)
841 .with_size(file_size)
842 .with_time(file_time)
843 } else {
844 let abs_dir = PathBuf::from(abs_path.parent().unwrap());
845 let rel_dir = PathBuf::from(rel_path.parent().unwrap());
846 let file_name = String::from(abs_path.file_name().and_then(OsStr::to_str).unwrap());
847 let file_ext = String::from(abs_path.extension().and_then(OsStr::to_str).unwrap_or_default());
848 File::new(abs_dir, rel_dir, file_depth, None, file_name, file_ext, file_type)
849 .with_size(file_size)
850 .with_time(file_time)
851 }
852 }
853
854 fn sort_files(mut files: Vec<File>, order: Vec<OrderKind>) -> Vec<String> {
855 let config = create_config(order);
856 let sorter = Sorter::new(&config);
857 sorter.sort_files(&mut files);
858 files.into_iter().map(format_file).collect()
859 }
860
861 fn format_file(file: File) -> String {
862 file.rel_dir
863 .join(file.file_name)
864 .to_str()
865 .map(|x| String::from(x))
866 .map(|x| x.replace(MAIN_SEPARATOR_STR, "/"))
867 .unwrap()
868 }
869
870 fn create_config(order: Vec<OrderKind>) -> Config {
871 Config::default().with_sort_order(order)
872 }
873
874 fn string_from(values: Vec<&str>) -> Vec<String> {
875 values.into_iter().map(String::from).collect()
876 }
877}