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 googletest::prelude::*;
115 use std::ffi::OsStr;
116 use std::path::{PathBuf, MAIN_SEPARATOR_STR};
117
118 #[gtest]
119 fn test_files_are_sorted_by_path() {
120 let files = create_files();
121 let paths = sort_files(files, vec![OrderKind::Dir, OrderKind::Name]);
122 expect_that!(paths, elements_are![
123 "index",
124 "cheese/",
125 "cheese/index",
126 "cheese/english/",
127 "cheese/english/cheddar.xy",
128 "cheese/english/stilton.ij",
129 "cheese/french/",
130 "cheese/french/bleu.xy",
131 "cheese/french/brie.ij",
132 "fruit/",
133 "fruit/citrus/",
134 "fruit/citrus/lemon.xy",
135 "fruit/citrus/orange.ij",
136 "fruit/other/",
137 "fruit/other/apple.xy",
138 "fruit/other/banana.ij",
139 "fruit/other/cherry.xy",
140 "fruit/other/date.ij",
141 "fruit.zip/",
142 "fruit.zip/citrus/",
143 "fruit.zip/citrus/lemon.xy",
144 "fruit.zip/citrus/orange.ij",
145 "fruit.zip/other/",
146 "fruit.zip/other/apple.xy",
147 "fruit.zip/other/banana.ij",
148 "fruit.zip/other/cherry.xy",
149 "fruit.zip/other/date.ij",
150 ]);
151 }
152
153 #[gtest]
154 fn test_files_are_sorted_by_dir_and_size() {
155 let files = create_files();
156 let paths = sort_files(files, vec![OrderKind::Dir, OrderKind::Size(DirKind::Asc), OrderKind::Name]);
157 expect_that!(paths, elements_are![
158 "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", ]);
186 }
187
188 #[gtest]
189 fn test_files_are_sorted_by_dir_and_time() {
190 let files = create_files();
191 let paths = sort_files(files, vec![OrderKind::Dir, OrderKind::Time(DirKind::Asc), OrderKind::Name]);
192 expect_that!(paths, elements_are![
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 }
222
223 #[gtest]
224 fn test_files_are_sorted_by_name() {
225 let files = create_files();
226 let paths = sort_files(files, vec![OrderKind::Name, OrderKind::Dir]);
227 expect_that!(paths, elements_are![
228 "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", ]);
256 }
257
258 #[gtest]
259 fn test_files_are_sorted_by_ext() {
260 let files = create_files();
261 let paths = sort_files(files, vec![OrderKind::Ext, OrderKind::Dir, OrderKind::Name]);
262 expect_that!(paths, elements_are![
263 "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", ]);
291 }
292
293 #[gtest]
294 fn test_files_are_sorted_by_ext_and_size() {
295 let files = create_files();
296 let paths = sort_files(files, vec![OrderKind::Ext, OrderKind::Size(DirKind::Asc), OrderKind::Dir, OrderKind::Name]);
297 expect_that!(paths, elements_are![
298 "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", ]);
326 }
327
328 #[gtest]
329 fn test_files_are_sorted_by_size_asc() {
330 let files = create_files();
331 let paths = sort_files(files, vec![OrderKind::Size(DirKind::Asc), OrderKind::Dir, OrderKind::Name]);
332 expect_that!(paths, elements_are![
333 "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", ]);
361 }
362
363 #[gtest]
364 fn test_files_are_sorted_by_size_desc() {
365 let files = create_files();
366 let paths = sort_files(files, vec![OrderKind::Size(DirKind::Desc), OrderKind::Dir, OrderKind::Name]);
367 expect_that!(paths, elements_are![
368 "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/", ]);
396 }
397
398 #[gtest]
399 fn test_files_are_sorted_by_time_asc() {
400 let files = create_files();
401 let paths = sort_files(files, vec![OrderKind::Time(DirKind::Asc), OrderKind::Dir, OrderKind::Name]);
402 expect_that!(paths, elements_are![
403 "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/", ]);
431 }
432
433 #[gtest]
434 fn test_files_are_sorted_by_time_desc() {
435 let files = create_files();
436 let paths = sort_files(files, vec![OrderKind::Time(DirKind::Desc), OrderKind::Dir, OrderKind::Name]);
437 expect_that!(paths, elements_are![
438 "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", ]);
466 }
467
468 #[gtest]
469 fn test_files_are_sorted_by_group() {
470 let files = create_files();
471 let paths = sort_files(files, vec![OrderKind::Group, OrderKind::Dir, OrderKind::Name]);
472 expect_that!(paths, elements_are![
473 "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", ]);
501 }
502
503 #[gtest]
504 fn test_files_are_sorted_by_case_insensitive_path() {
505 let files = create_case();
506 let paths = sort_files(files, vec![OrderKind::Dir, OrderKind::Name]);
507 expect_that!(paths, elements_are![
508 "AAA.DD",
509 "AAA.dd",
510 "aaa.DD",
511 "aaa.dd",
512 "AAA.EE",
513 "AAA.ee",
514 "aaa.EE",
515 "aaa.ee",
516 "AAA.FF",
517 "AAA.ff",
518 "aaa.FF",
519 "aaa.ff",
520 "BBB.DD",
521 "BBB.dd",
522 "bbb.DD",
523 "bbb.dd",
524 "BBB.EE",
525 "BBB.ee",
526 "bbb.EE",
527 "bbb.ee",
528 "BBB.FF",
529 "BBB.ff",
530 "bbb.FF",
531 "bbb.ff",
532 "CCC.DD",
533 "CCC.dd",
534 "ccc.DD",
535 "ccc.dd",
536 "CCC.EE",
537 "CCC.ee",
538 "ccc.EE",
539 "ccc.ee",
540 "CCC.FF",
541 "CCC.ff",
542 "ccc.FF",
543 "ccc.ff",
544 ]);
545 }
546
547 #[gtest]
548 fn test_files_are_sorted_by_case_insensitive_ext() {
549 let files = create_case();
550 let paths = sort_files(files, vec![OrderKind::Ext, OrderKind::Dir, OrderKind::Name]);
551 expect_that!(paths, elements_are![
552 "AAA.DD",
553 "aaa.DD",
554 "BBB.DD",
555 "bbb.DD",
556 "CCC.DD",
557 "ccc.DD",
558 "AAA.dd",
559 "aaa.dd",
560 "BBB.dd",
561 "bbb.dd",
562 "CCC.dd",
563 "ccc.dd",
564 "AAA.EE",
565 "aaa.EE",
566 "BBB.EE",
567 "bbb.EE",
568 "CCC.EE",
569 "ccc.EE",
570 "AAA.ee",
571 "aaa.ee",
572 "BBB.ee",
573 "bbb.ee",
574 "CCC.ee",
575 "ccc.ee",
576 "AAA.FF",
577 "aaa.FF",
578 "BBB.FF",
579 "bbb.FF",
580 "CCC.FF",
581 "ccc.FF",
582 "AAA.ff",
583 "aaa.ff",
584 "BBB.ff",
585 "bbb.ff",
586 "CCC.ff",
587 "ccc.ff",
588 ]);
589 }
590
591 #[gtest]
592 fn test_files_are_sorted_before_dirs() {
593 let files = create_folder();
594 let paths = sort_files(files, vec![OrderKind::Dir, OrderKind::Name]);
595 expect_that!(paths, elements_are![
596 "xyz",
597 "abc/",
598 "abc/xyz",
599 "abc/def/",
600 "abc/def/xyz",
601 ]);
602 }
603
604 #[gtest]
605 fn test_numeric_files_are_sorted_by_dir() {
606 let files = create_numeric1();
607 let paths = sort_files(files, vec![OrderKind::Dir, OrderKind::Name]);
608 expect_that!(paths, elements_are![
609 "9/",
610 "9/9/",
611 "9/9/9/",
612 "9/9/10/",
613 "9/10/",
614 "9/10/9/",
615 "9/10/10/",
616 "10/",
617 "10/9/",
618 "10/9/9/",
619 "10/9/10/",
620 "10/10/",
621 "10/10/9/",
622 "10/10/10/",
623 ]);
624 }
625
626 #[gtest]
627 fn test_numeric_files_are_sorted_by_name() {
628 let files = create_numeric2();
629 let paths = sort_files(files, vec![OrderKind::Dir, OrderKind::Name]);
630 expect_that!(paths, elements_are![
631 "9",
632 "9_9",
633 "9_9_9",
634 "9_9_10",
635 "9_10",
636 "9_10_9",
637 "9_10_10",
638 "10",
639 "10_9",
640 "10_9_9",
641 "10_9_10",
642 "10_10",
643 "10_10_9",
644 "10_10_10",
645 ]);
646 }
647
648 #[gtest]
649 fn test_numeric_files_are_sorted_by_ext() {
650 let files = create_numeric3();
651 let paths = sort_files(files, vec![OrderKind::Ext, OrderKind::Dir, OrderKind::Name]);
652 expect_that!(paths, elements_are![
653 "file.9",
654 "file.9_9",
655 "file.9_9_9",
656 "file.9_9_10",
657 "file.9_10",
658 "file.9_10_9",
659 "file.9_10_10",
660 "file.10",
661 "file.10_9",
662 "file.10_9_9",
663 "file.10_9_10",
664 "file.10_10",
665 "file.10_10_9",
666 "file.10_10_10",
667 ]);
668 }
669
670 fn create_files() -> Vec<File> {
671 vec![
672 create_file(FileKind::Other, "2023-07-05T13:05:42Z", 42, "index"),
673 create_file(FileKind::Dir, "2023-01-16T11:25:51Z", 0, "cheese"),
674 create_file(FileKind::Other, "2022-12-23T21:43:18Z", 99, "cheese/index"),
675 create_file(FileKind::Dir, "2023-08-04T10:57:26Z", 0, "cheese/english"),
676 create_file(FileKind::Other, "2022-09-10T04:03:38Z", 6363, "cheese/english/cheddar.xy"),
677 create_file(FileKind::Other, "2023-04-07T03:08:29Z", 91, "cheese/english/stilton.ij"),
678 create_file(FileKind::Dir, "2022-10-06T23:36:27Z", 0, "cheese/french"),
679 create_file(FileKind::Other, "2022-10-28T05:49:07Z", 20122, "cheese/french/bleu.xy"),
680 create_file(FileKind::Other, "2023-02-28T11:01:59Z", 8681, "cheese/french/brie.ij"),
681 create_file(FileKind::Dir, "2023-04-18T17:09:31Z", 0, "fruit"),
682 create_file(FileKind::Dir, "2023-07-13T08:58:00Z", 0, "fruit/citrus"),
683 create_file(FileKind::Other, "2023-06-29T21:16:08Z", 108, "fruit/citrus/lemon.xy"),
684 create_file(FileKind::Other, "2023-03-27T05:45:33Z", 173, "fruit/citrus/orange.ij"),
685 create_file(FileKind::Dir, "2023-08-14T01:53:00Z", 0, "fruit/other"),
686 create_file(FileKind::Other, "2023-05-10T11:24:22Z", 467, "fruit/other/apple.xy"),
687 create_file(FileKind::Other, "2023-06-10T02:06:38Z", 73, "fruit/other/banana.ij"),
688 create_file(FileKind::Other, "2022-12-19T02:12:38Z", 795, "fruit/other/cherry.xy"),
689 create_file(FileKind::Other, "2022-11-25T14:42:53Z", 6, "fruit/other/date.ij"),
690 create_file(FileKind::Dir, "2023-04-18T17:09:31Z", 0, "fruit.zip"),
691 create_file(FileKind::Dir, "2023-07-13T08:58:00Z", 0, "fruit.zip/citrus"),
692 create_file(FileKind::Other, "2023-06-29T21:16:08Z", 108, "fruit.zip/citrus/lemon.xy"),
693 create_file(FileKind::Other, "2023-03-27T05:45:33Z", 173, "fruit.zip/citrus/orange.ij"),
694 create_file(FileKind::Dir, "2023-08-14T01:53:00Z", 0, "fruit.zip/other"),
695 create_file(FileKind::Other, "2023-05-10T11:24:22Z", 467, "fruit.zip/other/apple.xy"),
696 create_file(FileKind::Other, "2023-06-10T02:06:38Z", 73, "fruit.zip/other/banana.ij"),
697 create_file(FileKind::Other, "2022-12-19T02:12:38Z", 795, "fruit.zip/other/cherry.xy"),
698 create_file(FileKind::Other, "2022-11-25T14:42:53Z", 6, "fruit.zip/other/date.ij"),
699 ]
700 }
701
702 fn create_case() -> Vec<File> {
703 vec![
704 create_file(FileKind::Other, "1970-01-01T00:00:00Z", 0, "aaa.dd"),
705 create_file(FileKind::Other, "1970-01-01T00:00:00Z", 0, "aaa.DD"),
706 create_file(FileKind::Other, "1970-01-01T00:00:00Z", 0, "aaa.ee"),
707 create_file(FileKind::Other, "1970-01-01T00:00:00Z", 0, "aaa.EE"),
708 create_file(FileKind::Other, "1970-01-01T00:00:00Z", 0, "aaa.ff"),
709 create_file(FileKind::Other, "1970-01-01T00:00:00Z", 0, "aaa.FF"),
710 create_file(FileKind::Other, "1970-01-01T00:00:00Z", 0, "AAA.dd"),
711 create_file(FileKind::Other, "1970-01-01T00:00:00Z", 0, "AAA.DD"),
712 create_file(FileKind::Other, "1970-01-01T00:00:00Z", 0, "AAA.ee"),
713 create_file(FileKind::Other, "1970-01-01T00:00:00Z", 0, "AAA.EE"),
714 create_file(FileKind::Other, "1970-01-01T00:00:00Z", 0, "AAA.ff"),
715 create_file(FileKind::Other, "1970-01-01T00:00:00Z", 0, "AAA.FF"),
716 create_file(FileKind::Other, "1970-01-01T00:00:00Z", 0, "bbb.dd"),
717 create_file(FileKind::Other, "1970-01-01T00:00:00Z", 0, "bbb.DD"),
718 create_file(FileKind::Other, "1970-01-01T00:00:00Z", 0, "bbb.ee"),
719 create_file(FileKind::Other, "1970-01-01T00:00:00Z", 0, "bbb.EE"),
720 create_file(FileKind::Other, "1970-01-01T00:00:00Z", 0, "bbb.ff"),
721 create_file(FileKind::Other, "1970-01-01T00:00:00Z", 0, "bbb.FF"),
722 create_file(FileKind::Other, "1970-01-01T00:00:00Z", 0, "BBB.dd"),
723 create_file(FileKind::Other, "1970-01-01T00:00:00Z", 0, "BBB.DD"),
724 create_file(FileKind::Other, "1970-01-01T00:00:00Z", 0, "BBB.ee"),
725 create_file(FileKind::Other, "1970-01-01T00:00:00Z", 0, "BBB.EE"),
726 create_file(FileKind::Other, "1970-01-01T00:00:00Z", 0, "BBB.ff"),
727 create_file(FileKind::Other, "1970-01-01T00:00:00Z", 0, "BBB.FF"),
728 create_file(FileKind::Other, "1970-01-01T00:00:00Z", 0, "ccc.dd"),
729 create_file(FileKind::Other, "1970-01-01T00:00:00Z", 0, "ccc.DD"),
730 create_file(FileKind::Other, "1970-01-01T00:00:00Z", 0, "ccc.ee"),
731 create_file(FileKind::Other, "1970-01-01T00:00:00Z", 0, "ccc.EE"),
732 create_file(FileKind::Other, "1970-01-01T00:00:00Z", 0, "ccc.ff"),
733 create_file(FileKind::Other, "1970-01-01T00:00:00Z", 0, "ccc.FF"),
734 create_file(FileKind::Other, "1970-01-01T00:00:00Z", 0, "CCC.dd"),
735 create_file(FileKind::Other, "1970-01-01T00:00:00Z", 0, "CCC.DD"),
736 create_file(FileKind::Other, "1970-01-01T00:00:00Z", 0, "CCC.ee"),
737 create_file(FileKind::Other, "1970-01-01T00:00:00Z", 0, "CCC.EE"),
738 create_file(FileKind::Other, "1970-01-01T00:00:00Z", 0, "CCC.ff"),
739 create_file(FileKind::Other, "1970-01-01T00:00:00Z", 0, "CCC.FF"),
740 ]
741 }
742
743 fn create_folder() -> Vec<File> {
744 vec![
745 create_file(FileKind::Dir, "1970-01-01T00:00:00Z", 0, "abc"),
746 create_file(FileKind::Dir, "1970-01-01T00:00:00Z", 0, "abc/def"),
747 create_file(FileKind::Other, "1970-01-01T00:00:00Z", 0, "abc/def/xyz"),
748 create_file(FileKind::Other, "1970-01-01T00:00:00Z", 0, "abc/xyz"),
749 create_file(FileKind::Other, "1970-01-01T00:00:00Z", 0, "xyz"),
750 ]
751 }
752
753 fn create_numeric1() -> Vec<File> {
754 vec![
755 create_file(FileKind::Dir, "1970-01-01T00:00:00Z", 0, "10"),
756 create_file(FileKind::Dir, "1970-01-01T00:00:00Z", 0, "10/10"),
757 create_file(FileKind::Dir, "1970-01-01T00:00:00Z", 0, "10/10/10"),
758 create_file(FileKind::Dir, "1970-01-01T00:00:00Z", 0, "10/10/9"),
759 create_file(FileKind::Dir, "1970-01-01T00:00:00Z", 0, "10/9"),
760 create_file(FileKind::Dir, "1970-01-01T00:00:00Z", 0, "10/9/10"),
761 create_file(FileKind::Dir, "1970-01-01T00:00:00Z", 0, "10/9/9"),
762 create_file(FileKind::Dir, "1970-01-01T00:00:00Z", 0, "9"),
763 create_file(FileKind::Dir, "1970-01-01T00:00:00Z", 0, "9/10"),
764 create_file(FileKind::Dir, "1970-01-01T00:00:00Z", 0, "9/10/10"),
765 create_file(FileKind::Dir, "1970-01-01T00:00:00Z", 0, "9/10/9"),
766 create_file(FileKind::Dir, "1970-01-01T00:00:00Z", 0, "9/9"),
767 create_file(FileKind::Dir, "1970-01-01T00:00:00Z", 0, "9/9/10"),
768 create_file(FileKind::Dir, "1970-01-01T00:00:00Z", 0, "9/9/9"),
769 ]
770 }
771
772 fn create_numeric2() -> Vec<File> {
773 vec![
774 create_file(FileKind::Other, "1970-01-01T00:00:00Z", 0, "10"),
775 create_file(FileKind::Other, "1970-01-01T00:00:00Z", 0, "10_10"),
776 create_file(FileKind::Other, "1970-01-01T00:00:00Z", 0, "10_10_10"),
777 create_file(FileKind::Other, "1970-01-01T00:00:00Z", 0, "10_10_9"),
778 create_file(FileKind::Other, "1970-01-01T00:00:00Z", 0, "10_9"),
779 create_file(FileKind::Other, "1970-01-01T00:00:00Z", 0, "10_9_10"),
780 create_file(FileKind::Other, "1970-01-01T00:00:00Z", 0, "10_9_9"),
781 create_file(FileKind::Other, "1970-01-01T00:00:00Z", 0, "9"),
782 create_file(FileKind::Other, "1970-01-01T00:00:00Z", 0, "9_10"),
783 create_file(FileKind::Other, "1970-01-01T00:00:00Z", 0, "9_10_10"),
784 create_file(FileKind::Other, "1970-01-01T00:00:00Z", 0, "9_10_9"),
785 create_file(FileKind::Other, "1970-01-01T00:00:00Z", 0, "9_9"),
786 create_file(FileKind::Other, "1970-01-01T00:00:00Z", 0, "9_9_10"),
787 create_file(FileKind::Other, "1970-01-01T00:00:00Z", 0, "9_9_9"),
788 ]
789 }
790
791 fn create_numeric3() -> Vec<File> {
792 vec![
793 create_file(FileKind::Other, "1970-01-01T00:00:00Z", 0, "file.10"),
794 create_file(FileKind::Other, "1970-01-01T00:00:00Z", 0, "file.10_10"),
795 create_file(FileKind::Other, "1970-01-01T00:00:00Z", 0, "file.10_10_10"),
796 create_file(FileKind::Other, "1970-01-01T00:00:00Z", 0, "file.10_10_9"),
797 create_file(FileKind::Other, "1970-01-01T00:00:00Z", 0, "file.10_9"),
798 create_file(FileKind::Other, "1970-01-01T00:00:00Z", 0, "file.10_9_10"),
799 create_file(FileKind::Other, "1970-01-01T00:00:00Z", 0, "file.10_9_9"),
800 create_file(FileKind::Other, "1970-01-01T00:00:00Z", 0, "file.9"),
801 create_file(FileKind::Other, "1970-01-01T00:00:00Z", 0, "file.9_10"),
802 create_file(FileKind::Other, "1970-01-01T00:00:00Z", 0, "file.9_10_10"),
803 create_file(FileKind::Other, "1970-01-01T00:00:00Z", 0, "file.9_10_9"),
804 create_file(FileKind::Other, "1970-01-01T00:00:00Z", 0, "file.9_9"),
805 create_file(FileKind::Other, "1970-01-01T00:00:00Z", 0, "file.9_9_10"),
806 create_file(FileKind::Other, "1970-01-01T00:00:00Z", 0, "file.9_9_9"),
807 ]
808 }
809
810 fn create_file(
811 file_type: FileKind,
812 file_time: &str,
813 file_size: u64,
814 rel_path: &str,
815 ) -> File {
816 let abs_path = PathBuf::from("/root/").join(rel_path);
817 let rel_path = PathBuf::from(rel_path);
818 let file_depth = rel_path.components().count();
819 let file_time = DateTime::parse_from_rfc3339(file_time).unwrap().with_timezone(&Utc);
820 if file_type == FileKind::Dir {
821 let file_name = String::from("");
822 let file_ext = String::from("");
823 File::new(abs_path, rel_path, file_depth, None, file_name, file_ext, file_type)
824 .with_size(file_size)
825 .with_time(file_time)
826 } else {
827 let abs_dir = PathBuf::from(abs_path.parent().unwrap());
828 let rel_dir = PathBuf::from(rel_path.parent().unwrap());
829 let file_name = String::from(abs_path.file_name().and_then(OsStr::to_str).unwrap());
830 let file_ext = String::from(abs_path.extension().and_then(OsStr::to_str).unwrap_or_default());
831 File::new(abs_dir, rel_dir, file_depth, None, file_name, file_ext, file_type)
832 .with_size(file_size)
833 .with_time(file_time)
834 }
835 }
836
837 fn sort_files(mut files: Vec<File>, order: Vec<OrderKind>) -> Vec<String> {
838 let config = create_config(order);
839 let sorter = Sorter::new(&config);
840 sorter.sort_files(&mut files);
841 files.into_iter().map(format_file).collect()
842 }
843
844 fn format_file(file: File) -> String {
845 file.rel_dir
846 .join(file.file_name)
847 .to_str()
848 .map(|x| String::from(x))
849 .map(|x| x.replace(MAIN_SEPARATOR_STR, "/"))
850 .unwrap()
851 }
852
853 fn create_config(order: Vec<OrderKind>) -> Config {
854 Config::default().with_sort_order(order)
855 }
856}