sort_path_length/
lib.rs

1use std::cmp::Ordering;
2use std::collections::HashMap;
3use std::io::Write;
4use std::path::Path;
5use std::str;
6
7///
8/// It reads the input as lines and parses each one as a path and then sorts them
9/// out by the length of their path components.
10/// Pass invert as true to invert the order
11///
12pub fn sort_path_length(input: &str, invert: bool, output: &mut dyn Write) {
13    let mut path_hash_map: HashMap<usize, Vec<&str>> = HashMap::new();
14
15    for line_path in input.lines() {
16        let components = Path::new(line_path).components().collect::<Vec<_>>();
17        path_hash_map
18            .entry(components.len())
19            .and_modify(|v| {
20                // Addding to the inner list of same length components in ordered manner
21                if invert {
22                    for (index, &l) in v.iter().enumerate() {
23                        if line_path.partial_cmp(l).unwrap() == Ordering::Greater {
24                            v.insert(index, line_path);
25                            break;
26                        }
27                    }
28                } else {
29                    for (index, &l) in v.iter().enumerate() {
30                        if line_path.partial_cmp(l).unwrap() == Ordering::Less {
31                            v.insert(index, line_path);
32                            // using a early return or shortcut to not use a
33                            // flag to check if the element is inserted after
34                            // the current elements loop
35                            return;
36                        }
37                    }
38
39                    v.push(line_path);
40                }
41            })
42            .or_insert(vec![line_path]);
43    }
44
45    let mut lengths = path_hash_map.keys().collect::<Vec<_>>();
46
47    if invert {
48        lengths.sort_by(|a, b| b.partial_cmp(a).unwrap());
49    } else {
50        lengths.sort();
51    }
52
53    // Writing to output in order
54    for k in lengths {
55        if let Some(paths) = path_hash_map.get(k) {
56            for &p in paths {
57                output.write(p.as_bytes()).unwrap();
58                output.write("\n".as_bytes()).unwrap();
59            }
60        };
61    }
62}
63
64#[test]
65pub fn test_default_sort_order() {
66    let input = "/a/absolute/path\n/a/b/c/d/e\n/a\n/a/dpasodj";
67
68    let mut output: Vec<u8> = Vec::new();
69
70    sort_path_length(input, false, &mut output);
71
72    let expected = "/a\n/a/dpasodj\n/a/absolute/path\n/a/b/c/d/e\n";
73
74    assert_eq!(expected, str::from_utf8(&output).unwrap());
75}
76
77#[test]
78pub fn test_inverted_sort_order() {
79    let input = "/a/absolute/path\n/a/b/c/d/e\n/a\n/a/dpasodj";
80
81    let mut output: Vec<u8> = Vec::new();
82
83    sort_path_length(input, true, &mut output);
84
85    let expected = "/a/b/c/d/e\n/a/absolute/path\n/a/dpasodj\n/a\n";
86
87    assert_eq!(expected, str::from_utf8(&output).unwrap());
88}
89
90#[test]
91pub fn test_default_sort_order_same_length_paths() {
92    let input = "/a/b/c/d/e
93/b/c/d/e/f
94/c/d/e/f/g
95/a/e/d/c/b
96/b/f/e/c/d
97";
98
99    let mut output: Vec<u8> = Vec::new();
100
101    sort_path_length(input, false, &mut output);
102
103    let expected = "/a/b/c/d/e
104/a/e/d/c/b
105/b/c/d/e/f
106/b/f/e/c/d
107/c/d/e/f/g
108";
109
110    assert_eq!(expected, str::from_utf8(&output).unwrap());
111}
112
113#[test]
114pub fn test_inverted_sort_order_same_length_paths() {
115    let input = "/a/b/c/d/e
116/b/c/d/e/f
117/c/d/e/f/g
118/a/e/d/c/b
119/b/f/e/c/d
120";
121
122    let mut output: Vec<u8> = Vec::new();
123    sort_path_length(input, true, &mut output);
124
125    let expected = "/c/d/e/f/g
126/b/f/e/c/d
127/b/c/d/e/f
128/a/e/d/c/b
129/a/b/c/d/e
130";
131
132    assert_eq!(expected, str::from_utf8(&output).unwrap());
133}