zellij_utils/
common_path.rs

1// The following license refers to code in this file and this file only.
2// We chose to vendor this dependency rather than depend on it through crates.io in order to facilitate
3// packaging. This license was copied verbatim from: https://docs.rs/crate/common-path/1.0.0/source/LICENSE-MIT
4//
5// MIT License
6//
7// Copyright 2018 Paul Woolcock <paul@woolcock.us>
8//
9// Permission is hereby granted, free of charge, to any person obtaining a copy of
10// this software and associated documentation files (the "Software"), to deal in
11// the Software without restriction, including without limitation the rights to
12// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
13// of the Software, and to permit persons to whom the Software is furnished to do
14// so, subject to the following conditions:
15//
16// The above copyright notice and this permission notice shall be included in all
17// copies or substantial portions of the Software.
18//
19// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
25// SOFTWARE.
26
27use std::path::{Path, PathBuf};
28
29/// Find the common prefix, if any, between any number of paths
30///
31/// # Example
32///
33/// ```rust
34/// use std::path::{PathBuf, Path};
35/// use zellij_utils::common_path::common_path_all;
36///
37/// # fn main() {
38/// let baz = Path::new("/foo/bar/baz");
39/// let quux = Path::new("/foo/bar/quux");
40/// let foo = Path::new("/foo/bar/foo");
41/// let prefix = common_path_all(vec![baz, quux, foo]).unwrap();
42/// assert_eq!(prefix, Path::new("/foo/bar").to_path_buf());
43/// # }
44/// ```
45pub fn common_path_all<'a>(paths: impl IntoIterator<Item = &'a Path>) -> Option<PathBuf> {
46    let mut path_iter = paths.into_iter();
47    let mut result = path_iter.next()?.to_path_buf();
48    for path in path_iter {
49        if let Some(r) = common_path(&result, &path) {
50            result = r;
51        } else {
52            return None;
53        }
54    }
55    Some(result.to_path_buf())
56}
57
58/// Find the common prefix, if any, between 2 paths
59///
60/// # Example
61///
62/// ```rust
63/// use std::path::{PathBuf, Path};
64/// use zellij_utils::common_path::common_path;
65///
66/// # fn main() {
67/// let baz = Path::new("/foo/bar/baz");
68/// let quux = Path::new("/foo/bar/quux");
69/// let prefix = common_path(baz, quux).unwrap();
70/// assert_eq!(prefix, Path::new("/foo/bar").to_path_buf());
71/// # }
72/// ```
73pub fn common_path<P, Q>(one: P, two: Q) -> Option<PathBuf>
74where
75    P: AsRef<Path>,
76    Q: AsRef<Path>,
77{
78    let one = one.as_ref();
79    let two = two.as_ref();
80    let one = one.components();
81    let two = two.components();
82    let mut final_path = PathBuf::new();
83    let mut found = false;
84    let paths = one.zip(two);
85    for (l, r) in paths {
86        if l == r {
87            final_path.push(l.as_os_str());
88            found = true;
89        } else {
90            break;
91        }
92    }
93    if found {
94        Some(final_path)
95    } else {
96        None
97    }
98}
99
100#[cfg(test)]
101mod tests {
102    use super::*;
103
104    #[test]
105    fn compare_all_paths() {
106        let one = Path::new("/foo/bar/baz/one.txt");
107        let two = Path::new("/foo/bar/quux/quuux/two.txt");
108        let three = Path::new("/foo/bar/baz/foo/bar");
109        let result = Path::new("/foo/bar");
110        let path_permutations = vec![
111            vec![one, two, three],
112            vec![one, three, two],
113            vec![two, one, three],
114            vec![two, three, one],
115            vec![three, one, two],
116            vec![three, two, one],
117        ];
118        for all in path_permutations {
119            assert_eq!(common_path_all(all).unwrap(), result.to_path_buf())
120        }
121    }
122
123    #[test]
124    fn compare_paths() {
125        let one = Path::new("/foo/bar/baz/one.txt");
126        let two = Path::new("/foo/bar/quux/quuux/two.txt");
127        let result = Path::new("/foo/bar");
128        assert_eq!(common_path(&one, &two).unwrap(), result.to_path_buf())
129    }
130
131    #[test]
132    fn no_common_path() {
133        let one = Path::new("/foo/bar");
134        let two = Path::new("./baz/quux");
135        assert!(common_path(&one, &two).is_none());
136    }
137}