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
// Copyright 2012-2015 The Rust Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution and at // http://rust-lang.org/COPYRIGHT. // // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your // option. This file may not be copied, modified, or distributed // except according to those terms. // Adapted from rustc's path_relative_from // https://github.com/rust-lang/rust/blob/e1d0de82cc40b666b88d4a6d2c9dcbc81d7ed27f/src/librustc_back/rpath.rs#L116-L158 use std::path::*; /// Construct a relative path from a provided base directory path to the provided path. /// /// ```rust /// use pathdiff::diff_paths; /// use std::path::*; /// /// let baz = "/foo/bar/baz"; /// let bar = "/foo/bar"; /// let quux = "/foo/bar/quux"; /// assert_eq!(diff_paths(bar, baz), Some("../".into())); /// assert_eq!(diff_paths(baz, bar), Some("baz".into())); /// assert_eq!(diff_paths(quux, baz), Some("../quux".into())); /// assert_eq!(diff_paths(baz, quux), Some("../baz".into())); /// assert_eq!(diff_paths(bar, quux), Some("../".into())); /// /// assert_eq!(diff_paths(&baz, &bar.to_string()), Some("baz".into())); /// assert_eq!(diff_paths(Path::new(baz), Path::new(bar).to_path_buf()), Some("baz".into())); /// ``` pub fn diff_paths<P, B>(path: P, base: B) -> Option<PathBuf> where P: AsRef<Path>, B: AsRef<Path>, { let path = path.as_ref(); let base = base.as_ref(); if path.is_absolute() != base.is_absolute() { if path.is_absolute() { Some(PathBuf::from(path)) } else { None } } else { let mut ita = path.components(); let mut itb = base.components(); let mut comps: Vec<Component> = vec![]; loop { match (ita.next(), itb.next()) { (None, None) => break, (Some(a), None) => { comps.push(a); comps.extend(ita.by_ref()); break; } (None, _) => comps.push(Component::ParentDir), (Some(a), Some(b)) if comps.is_empty() && a == b => (), (Some(a), Some(b)) if b == Component::CurDir => comps.push(a), (Some(_), Some(b)) if b == Component::ParentDir => return None, (Some(a), Some(_)) => { comps.push(Component::ParentDir); for _ in itb { comps.push(Component::ParentDir); } comps.push(a); comps.extend(ita.by_ref()); break; } } } Some(comps.iter().map(|c| c.as_os_str()).collect()) } }