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