1use ignore::{types::TypesBuilder, WalkBuilder};
2use std::cmp;
3use std::path::{Path, PathBuf};
4
5#[cfg(any(unix, windows))]
6const SAME_FS_SUPPORTED: bool = true;
7
8#[cfg(not(any(unix, windows)))]
9const SAME_FS_SUPPORTED: bool = false;
10
11pub fn search<P: AsRef<Path>>(basedir: P, pattern: &str) -> Vec<PathBuf> {
13 let mut builder = TypesBuilder::new();
14 builder.add("files", pattern).expect("Invalid file pattern");
15 let types = builder
16 .select("files")
17 .build()
18 .expect("Invalid Types definition");
19 let walker = WalkBuilder::new(basedir)
20 .follow_links(true)
21 .same_file_system(SAME_FS_SUPPORTED)
22 .types(types)
23 .build();
24 let mut files = Vec::new();
25 for entry in walker.flatten() {
26 if !entry.file_type().expect("stdin not supported").is_dir() {
27 files.push(entry.path().to_path_buf());
28 }
29 }
30 files
31}
32
33pub fn longest_common_prefix(paths: &Vec<PathBuf>) -> PathBuf {
36 if paths.is_empty() {
37 return PathBuf::new();
38 }
39 let path0 = &paths[0];
40 let mut len = path0.components().count();
41 for path in paths {
42 len = cmp::min(
43 len,
44 path.components()
45 .take(len)
46 .zip(path0.components())
47 .take_while(|&(a, b)| a == b)
48 .count(),
49 );
50 }
51 let common: Vec<_> = path0
52 .components()
53 .take(len)
54 .map(|comp| comp.as_os_str())
55 .collect();
56 common.iter().collect()
57}
58
59#[cfg(test)]
60mod test {
61 use super::*;
62
63 #[test]
64 fn empty_lcp() {
65 assert_eq!(longest_common_prefix(&vec![]), PathBuf::new());
66 }
67
68 #[test]
69 fn single_lcp() {
70 assert_eq!(
71 longest_common_prefix(&vec![PathBuf::from("/a/b")]),
72 PathBuf::from("/a/b")
73 );
74 }
75
76 #[test]
77 fn no_lcp() {
78 assert_eq!(
79 longest_common_prefix(&vec![
80 PathBuf::from("/a"),
81 PathBuf::from("/b"),
82 PathBuf::from("/c")
83 ]),
84 PathBuf::from("/")
85 );
86 }
87
88 #[test]
89 fn valid_lcp() {
90 assert_eq!(
91 longest_common_prefix(&vec![
92 PathBuf::from("/a/b/a"),
93 PathBuf::from("/a/b/b"),
94 PathBuf::from("/a/b/c")
95 ]),
96 PathBuf::from("/a/b")
97 );
98 }
99
100 #[test]
101 fn valid_is_shortest_lcp() {
102 assert_eq!(
103 longest_common_prefix(&vec![
104 PathBuf::from("/a/b/a"),
105 PathBuf::from("/a/b"),
106 PathBuf::from("/a/b/c")
107 ]),
108 PathBuf::from("/a/b")
109 );
110 }
111
112 #[test]
113 fn lcp_files() {
114 assert_eq!(
115 longest_common_prefix(&vec![
116 PathBuf::from("/a/b/c/f1.x"),
117 PathBuf::from("/a/b/f2.x"),
118 PathBuf::from("/a/f3.x")
119 ]),
120 PathBuf::from("/a")
121 );
122 assert_eq!(
123 longest_common_prefix(&vec![
124 PathBuf::from("/a/f3.x"),
125 PathBuf::from("/a/b/c/f1.x"),
126 PathBuf::from("/a/b/f2.x")
127 ]),
128 PathBuf::from("/a")
129 );
130 assert_eq!(
131 longest_common_prefix(&vec![PathBuf::from("/a/f1.x"),]),
132 PathBuf::from("/a/f1.x") );
134 }
135}