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