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}