sugar_path 3.0.0

Host-native lexical path manipulation with borrowed and owned-buffer APIs
Documentation
use std::{borrow::Cow, path::Path};

use sugar_path::SugarPath;

fn assert_borrows_from_target(target: &Path, relative: &Path) {
  let target_bytes = target.as_os_str().as_encoded_bytes();
  let relative_bytes = relative.as_os_str().as_encoded_bytes();
  let target_start = target_bytes.as_ptr() as usize;
  let target_end = target_start + target_bytes.len();
  let relative_start = relative_bytes.as_ptr() as usize;
  let relative_end = relative_start + relative_bytes.len();
  assert!(relative_start >= target_start && relative_end <= target_end);
}

#[cfg(target_family = "unix")]
#[test]
fn unix_descendants_borrow_the_target_suffix() {
  for (target, base, expected) in [
    (
      "/workspace/rolldown/crates/rolldown/src/module_loader/module_task.rs",
      "/workspace/rolldown",
      "crates/rolldown/src/module_loader/module_task.rs",
    ),
    (
      "/workspace/rolldown/crates/rolldown/src/module_loader/module_task.rs",
      "/workspace/rolldown/crates/rolldown/src/module_loader",
      "module_task.rs",
    ),
    ("/workspace/rolldown/src/", "/workspace/rolldown", "src"),
    ("/workspace/rolldown", "/workspace/rolldown", ""),
  ] {
    let target = Path::new(target);
    let Cow::Borrowed(relative) = target.relative(base) else {
      panic!("expected target {target:?} relative to {base:?} to borrow");
    };
    assert_eq!(relative.as_os_str().as_encoded_bytes(), expected.as_bytes());
    assert_borrows_from_target(target, relative);
  }
}

#[cfg(target_family = "unix")]
#[test]
fn unix_upward_dirty_and_invalid_results_remain_owned() {
  for (target, base) in [
    (
      "/workspace/rolldown/crates/rolldown/src/bundle/bundle.rs",
      "/workspace/rolldown/crates/rolldown/src/module_loader",
    ),
    ("/workspace/rolldown/src/./index.js", "/workspace/rolldown"),
  ] {
    let target = Path::new(target);
    let relative = target.relative(base);
    assert!(matches!(relative, Cow::Owned(_)), "target {target:?}, base {base:?}");
  }

  use std::{ffi::OsString, os::unix::ffi::OsStringExt, path::PathBuf};
  let target = PathBuf::from(OsString::from_vec(b"/workspace/rolldown/invalid-\x80.js".to_vec()));
  let relative = target.relative("/workspace/rolldown");
  assert!(matches!(relative, Cow::Owned(_)));
}

#[cfg(target_family = "unix")]
#[test]
fn unix_descendants_with_redundant_internal_separators_are_owned() {
  let target = "/workspace/rolldown/src//index.js";
  let base = "/workspace/rolldown";
  let relative = Path::new(target).relative(base);
  assert!(matches!(relative, Cow::Owned(_)), "target {target:?}, base {base:?}");
  assert_eq!(relative.as_os_str().as_encoded_bytes(), b"src/index.js");
}

#[cfg(target_family = "unix")]
#[test]
fn string_values_borrow_through_the_str_impl() {
  let target = String::from("/workspace/rolldown/src/index.js");
  let Cow::Borrowed(relative) = target.relative("/workspace/rolldown") else {
    panic!("expected String target suffix to borrow");
  };
  assert_eq!(relative, Path::new("src/index.js"));
  assert_borrows_from_target(Path::new(&target), relative);
}

#[cfg(target_family = "unix")]
#[test]
fn borrowed_str_helpers_preserve_the_input_lifetime() {
  fn relative<'a>(target: &'a str, base: &Path) -> Cow<'a, Path> {
    target.relative(base)
  }

  let target = String::from("/workspace/rolldown/src/index.js");
  let relative = relative(&target, Path::new("/workspace/rolldown"));
  assert!(matches!(relative, Cow::Borrowed(_)));
  assert_eq!(relative, Path::new("src/index.js"));
}

#[cfg(target_family = "windows")]
#[test]
fn windows_native_descendants_borrow_the_target_suffix() {
  for (target, base, expected) in [
    (r"C:\workspace\rolldown\src\index.js", r"C:\workspace\rolldown", r"src\index.js"),
    (r"C:\workspace\rolldown/src\index.js", r"C:\workspace\rolldown", r"src\index.js"),
    (r"C:\workspace\rolldown\src\index.js", "c:/workspace/rolldown/src", "index.js"),
    (r"\\server\share\packages\app\index.js", r"\\server\share", r"packages\app\index.js"),
    (
      r"\\?\UNC\server\share\packages\app\index.js",
      r"\\?\UNC\server\share",
      r"packages\app\index.js",
    ),
    (r"\\?\C:\workspace\rolldown\src\index.js", r"\\?\C:\workspace\rolldown", r"src\index.js"),
    (r"C:\workspace\rolldown\src\", r"C:\workspace\rolldown", r"src"),
    (r"C:\workspace\rolldown", r"C:\workspace\rolldown", ""),
    ("C:/workspace/rolldown", "C:/workspace/rolldown", ""),
  ] {
    let target = Path::new(target);
    let Cow::Borrowed(relative) = target.relative(base) else {
      panic!("expected target {target:?} relative to {base:?} to borrow");
    };
    assert_eq!(relative.as_os_str().as_encoded_bytes(), expected.as_bytes());
    assert_borrows_from_target(target, relative);
  }
}

#[cfg(target_family = "windows")]
#[test]
fn windows_noncanonical_descendants_are_canonical_and_owned() {
  let target = r"C:\workspace\rolldown\src\\index.js";
  let base = r"C:\workspace\rolldown";
  let relative = Path::new(target).relative(base);
  assert!(matches!(relative, Cow::Owned(_)), "target {target:?}, base {base:?}");
  assert_eq!(relative.as_os_str().as_encoded_bytes(), br"src\index.js");
}

#[cfg(target_family = "windows")]
#[test]
fn windows_owned_results_preserve_relative_semantics() {
  for (target, base) in [
    ("C:/workspace/rolldown/src/index.js", "C:/workspace/rolldown"),
    (r"C:\workspace\rolldown\src/index.js", r"C:\workspace\rolldown"),
    (r"C:\workspace\rolldown\src\index.js", r"C:\workspace\rolldown\dist"),
    (r"\\server\other\packages\app\index.js", r"\\server\share"),
    (r"dist\assets\index.js", r"dist\chunks"),
    (r"C:\workspace\rolldown\.\src\index.js", r"C:\workspace\rolldown"),
  ] {
    let target = Path::new(target);
    let relative = target.relative(base);
    assert!(matches!(relative, Cow::Owned(_)), "target {target:?}, base {base:?}");
  }
}