common_path/lib.rs
1//! Calculates the common prefix for a set of paths, if a common prefix exists
2//!
3//! # Example
4//!
5//! ```rust
6//! # extern crate common_path;
7//! use std::path::{PathBuf, Path};
8//! use common_path::common_path;
9//!
10//! # fn main() {
11//! let baz = Path::new("/foo/bar/baz");
12//! let quux = Path::new("/foo/bar/quux");
13//! let prefix = common_path(baz, quux).unwrap();
14//! assert_eq!(prefix, Path::new("/foo/bar").to_path_buf());
15//! # }
16//! ```
17//!
18//! Or for more than 2 paths:
19//!
20//! ```rust
21//! # extern crate common_path;
22//! use std::path::{PathBuf, Path};
23//! use common_path::common_path_all;
24//!
25//! # fn main() {
26//! let baz = Path::new("/foo/bar/baz");
27//! let quux = Path::new("/foo/bar/quux");
28//! let foo = Path::new("/foo/bar/foo");
29//! let prefix = common_path_all(vec![baz, quux, foo]).unwrap();
30//! assert_eq!(prefix, Path::new("/foo/bar").to_path_buf());
31//! # }
32//! ```
33#[cfg(test)]
34extern crate rand;
35
36use std::path::{Path, PathBuf};
37
38/// Find the common prefix, if any, between any number of paths
39///
40/// # Example
41///
42/// ```rust
43/// # extern crate common_path;
44/// use std::path::{PathBuf, Path};
45/// use common_path::common_path_all;
46///
47/// # fn main() {
48/// let baz = Path::new("/foo/bar/baz");
49/// let quux = Path::new("/foo/bar/quux");
50/// let foo = Path::new("/foo/bar/foo");
51/// let prefix = common_path_all(vec![baz, quux, foo]).unwrap();
52/// assert_eq!(prefix, Path::new("/foo/bar").to_path_buf());
53/// # }
54/// ```
55pub fn common_path_all<'a>(paths: impl IntoIterator<Item = &'a Path>) -> Option<PathBuf> {
56 let mut path_iter = paths.into_iter();
57 let mut result = path_iter.next()?.to_path_buf();
58 for path in path_iter {
59 if let Some(r) = common_path(&result, &path) {
60 result = r;
61 } else {
62 return None;
63 }
64 }
65 Some(result.to_path_buf())
66}
67
68/// Find the common prefix, if any, between 2 paths
69///
70/// # Example
71///
72/// ```rust
73/// # extern crate common_path;
74/// use std::path::{PathBuf, Path};
75/// use common_path::common_path;
76///
77/// # fn main() {
78/// let baz = Path::new("/foo/bar/baz");
79/// let quux = Path::new("/foo/bar/quux");
80/// let prefix = common_path(baz, quux).unwrap();
81/// assert_eq!(prefix, Path::new("/foo/bar").to_path_buf());
82/// # }
83/// ```
84pub fn common_path<P, Q>(one: P, two: Q) -> Option<PathBuf>
85where
86 P: AsRef<Path>,
87 Q: AsRef<Path>,
88{
89 let one = one.as_ref();
90 let two = two.as_ref();
91 let one = one.components();
92 let two = two.components();
93 let mut final_path = PathBuf::new();
94 let mut found = false;
95 let paths = one.zip(two);
96 for (l, r) in paths {
97 if l == r {
98 final_path.push(l.as_os_str());
99 found = true;
100 } else {
101 break;
102 }
103 }
104 if found {
105 Some(final_path)
106 } else {
107 None
108 }
109}
110
111#[cfg(test)]
112mod tests {
113 use super::*;
114 use rand::{
115 thread_rng,
116 seq::SliceRandom,
117 };
118
119 #[test]
120 fn compare_all_paths() {
121 let mut rng = thread_rng();
122 for _ in 0..6 {
123 let one = Path::new("/foo/bar/baz/one.txt");
124 let two = Path::new("/foo/bar/quux/quuux/two.txt");
125 let three = Path::new("/foo/bar/baz/foo/bar");
126 let result = Path::new("/foo/bar");
127 let mut all = vec![one, two, three];
128 all.shuffle(&mut rng);
129 assert_eq!(common_path_all(all).unwrap(), result.to_path_buf())
130 }
131 }
132
133 #[test]
134 fn compare_paths() {
135 let one = Path::new("/foo/bar/baz/one.txt");
136 let two = Path::new("/foo/bar/quux/quuux/two.txt");
137 let result = Path::new("/foo/bar");
138 assert_eq!(common_path(&one, &two).unwrap(), result.to_path_buf())
139 }
140
141 #[test]
142 fn no_common_path() {
143 let one = Path::new("/foo/bar");
144 let two = Path::new("./baz/quux");
145 assert!(common_path(&one, &two).is_none());
146 }
147}