|
| 1 | +use std::path::Component; |
1 | 2 | use std::{
|
2 | 3 | borrow::Cow,
|
3 | 4 | ffi::{OsStr, OsString},
|
@@ -288,3 +289,48 @@ pub fn normalize<'a>(path: Cow<'a, Path>, current_dir: &Path) -> Option<Cow<'a,
|
288 | 289 | }
|
289 | 290 | .into()
|
290 | 291 | }
|
| 292 | + |
| 293 | +/// Rebuild the worktree-relative `relative_path` to be relative to `prefix`, which is the worktree-relative |
| 294 | +/// path equivalent to the position of the user, or current working directory. |
| 295 | +/// This is a no-op if `prefix` is empty. |
| 296 | +/// |
| 297 | +/// Note that both `relative_path` and `prefix` are assumed to be [normalized](normalize()), and failure to do so |
| 298 | +/// will lead to incorrect results. |
| 299 | +/// |
| 300 | +/// Note that both input paths are expected to be equal in terms of case too, as comparisons will be case-sensitive. |
| 301 | +pub fn relativize_with_prefix<'a>(relative_path: &'a Path, prefix: &Path) -> Cow<'a, Path> { |
| 302 | + if prefix.as_os_str().is_empty() { |
| 303 | + return Cow::Borrowed(relative_path); |
| 304 | + } |
| 305 | + debug_assert!( |
| 306 | + relative_path.components().all(|c| matches!(c, Component::Normal(_))), |
| 307 | + "BUG: all input is expected to be normalized, but relative_path was not" |
| 308 | + ); |
| 309 | + debug_assert!( |
| 310 | + prefix.components().all(|c| matches!(c, Component::Normal(_))), |
| 311 | + "BUG: all input is expected to be normalized, but prefix was not" |
| 312 | + ); |
| 313 | + |
| 314 | + let mut buf = PathBuf::new(); |
| 315 | + let mut rpc = relative_path.components().peekable(); |
| 316 | + let mut equal_thus_far = true; |
| 317 | + for pcomp in prefix.components() { |
| 318 | + if equal_thus_far { |
| 319 | + if let (Component::Normal(pname), Some(Component::Normal(rpname))) = (pcomp, rpc.peek()) { |
| 320 | + if &pname == rpname { |
| 321 | + rpc.next(); |
| 322 | + continue; |
| 323 | + } else { |
| 324 | + equal_thus_far = false; |
| 325 | + } |
| 326 | + } |
| 327 | + } |
| 328 | + buf.push(Component::ParentDir); |
| 329 | + } |
| 330 | + buf.extend(rpc); |
| 331 | + if buf.as_os_str().is_empty() { |
| 332 | + Cow::Borrowed(Path::new(".")) |
| 333 | + } else { |
| 334 | + Cow::Owned(buf) |
| 335 | + } |
| 336 | +} |
0 commit comments