use crate::{with_tmp_pool, Canonical};
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Dirent(std::path::PathBuf);
impl Dirent {
pub fn new(path: impl AsRef<std::path::Path>) -> Result<Self, crate::Error<'static>> {
let canonical = canonicalize_dirent(path.as_ref())?;
Ok(Dirent(canonical))
}
pub fn as_path(&self) -> &std::path::Path {
&self.0
}
pub fn as_str(&self) -> &str {
self.0.to_str().unwrap_or("")
}
pub fn canonical(&self) -> Canonical<Dirent> {
Canonical(self.clone())
}
pub fn join(&self, component: impl AsRef<std::path::Path>) -> Result<Dirent, crate::Error<'_>> {
join_dirents(self, component)
}
pub fn basename(&self) -> Result<Dirent, crate::Error<'_>> {
basename_dirent(self)
}
pub fn dirname(&self) -> Result<Dirent, crate::Error<'_>> {
dirname_dirent(self)
}
pub fn split(&self) -> Result<(Dirent, Dirent), crate::Error<'_>> {
split_dirent(self)
}
pub fn is_absolute(&self) -> bool {
is_absolute_dirent(self).unwrap_or(false)
}
pub fn is_root(&self) -> bool {
is_root_dirent(self).unwrap_or(false)
}
pub fn is_canonical(&self) -> bool {
is_canonical_dirent(self).unwrap_or(true)
}
pub fn get_absolute(&self) -> Result<Dirent, crate::Error<'_>> {
get_absolute_dirent(self)
}
pub fn to_file_url(&self) -> Result<crate::uri::Uri, crate::Error<'static>> {
with_tmp_pool(|pool| unsafe {
let path_cstr = std::ffi::CString::new(self.as_str())?;
let mut url_ptr: *const std::ffi::c_char = std::ptr::null();
let err = subversion_sys::svn_uri_get_file_url_from_dirent(
&mut url_ptr,
path_cstr.as_ptr(),
pool.as_mut_ptr(),
);
crate::Error::from_raw(err)?;
let url_cstr = std::ffi::CStr::from_ptr(url_ptr);
let url_str = url_cstr.to_str()?;
crate::uri::Uri::new(url_str)
})
}
}
impl std::fmt::Display for Dirent {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.0.display().fmt(f)
}
}
impl AsRef<std::path::Path> for Dirent {
fn as_ref(&self) -> &std::path::Path {
&self.0
}
}
impl From<std::path::PathBuf> for Dirent {
fn from(path: std::path::PathBuf) -> Self {
Dirent(path)
}
}
pub fn canonicalize_dirent(
path: &std::path::Path,
) -> Result<std::path::PathBuf, crate::Error<'static>> {
with_tmp_pool(
|pool| -> Result<std::path::PathBuf, crate::Error<'static>> {
unsafe {
let path_str = path
.to_str()
.ok_or_else(|| crate::Error::from_message("Invalid path encoding"))?;
let path_cstr = std::ffi::CString::new(path_str)?;
let canonical =
subversion_sys::svn_dirent_canonicalize(path_cstr.as_ptr(), pool.as_mut_ptr());
let canonical_cstr = std::ffi::CStr::from_ptr(canonical);
let canonical_str = canonical_cstr.to_str()?;
Ok(std::path::PathBuf::from(canonical_str))
}
},
)
}
pub trait AsCanonicalDirent {
fn as_canonical_dirent(&self) -> Result<Canonical<Dirent>, crate::Error<'static>>;
}
impl AsCanonicalDirent for Dirent {
fn as_canonical_dirent(&self) -> Result<Canonical<Dirent>, crate::Error<'static>> {
Ok(self.canonical())
}
}
impl AsCanonicalDirent for Canonical<Dirent> {
fn as_canonical_dirent(&self) -> Result<Canonical<Dirent>, crate::Error<'static>> {
Ok(self.clone())
}
}
impl AsCanonicalDirent for &str {
fn as_canonical_dirent(&self) -> Result<Canonical<Dirent>, crate::Error<'static>> {
let dirent = Dirent::new(std::path::Path::new(self))?;
Ok(dirent.canonical())
}
}
impl AsCanonicalDirent for String {
fn as_canonical_dirent(&self) -> Result<Canonical<Dirent>, crate::Error<'static>> {
self.as_str().as_canonical_dirent()
}
}
impl AsCanonicalDirent for &std::path::Path {
fn as_canonical_dirent(&self) -> Result<Canonical<Dirent>, crate::Error<'static>> {
let dirent = Dirent::new(self)?;
Ok(dirent.canonical())
}
}
impl AsCanonicalDirent for std::path::PathBuf {
fn as_canonical_dirent(&self) -> Result<Canonical<Dirent>, crate::Error<'static>> {
self.as_path().as_canonical_dirent()
}
}
impl AsCanonicalDirent for &std::path::PathBuf {
fn as_canonical_dirent(&self) -> Result<Canonical<Dirent>, crate::Error<'static>> {
self.as_path().as_canonical_dirent()
}
}
pub fn join_dirents(
base: &Dirent,
component: impl AsRef<std::path::Path>,
) -> Result<Dirent, crate::Error<'static>> {
with_tmp_pool(|pool| unsafe {
let component_str = component
.as_ref()
.to_str()
.ok_or_else(|| crate::Error::from_message("Invalid component path"))?;
let base_cstr = std::ffi::CString::new(base.as_str())?;
let component_cstr = std::ffi::CString::new(component_str)?;
let joined = subversion_sys::svn_dirent_join(
base_cstr.as_ptr(),
component_cstr.as_ptr(),
pool.as_mut_ptr(),
);
let joined_cstr = std::ffi::CStr::from_ptr(joined);
let joined_str = joined_cstr.to_str()?;
Ok(Dirent(std::path::PathBuf::from(joined_str)))
})
}
pub fn basename_dirent(dirent: &Dirent) -> Result<Dirent, crate::Error<'_>> {
with_tmp_pool(|pool| unsafe {
let path_cstr = std::ffi::CString::new(dirent.as_str())?;
let basename = subversion_sys::svn_dirent_basename(path_cstr.as_ptr(), pool.as_mut_ptr());
let basename_cstr = std::ffi::CStr::from_ptr(basename);
let basename_str = basename_cstr.to_str()?;
Ok(Dirent(std::path::PathBuf::from(basename_str)))
})
}
pub fn dirname_dirent(dirent: &Dirent) -> Result<Dirent, crate::Error<'_>> {
with_tmp_pool(|pool| unsafe {
let path_cstr = std::ffi::CString::new(dirent.as_str())?;
let dirname = subversion_sys::svn_dirent_dirname(path_cstr.as_ptr(), pool.as_mut_ptr());
let dirname_cstr = std::ffi::CStr::from_ptr(dirname);
let dirname_str = dirname_cstr.to_str()?;
Ok(Dirent(std::path::PathBuf::from(dirname_str)))
})
}
pub fn split_dirent(dirent: &Dirent) -> Result<(Dirent, Dirent), crate::Error<'_>> {
with_tmp_pool(|pool| unsafe {
let path_cstr = std::ffi::CString::new(dirent.as_str())?;
let mut dirname_ptr: *const std::ffi::c_char = std::ptr::null();
let mut basename_ptr: *const std::ffi::c_char = std::ptr::null();
subversion_sys::svn_dirent_split(
&mut dirname_ptr,
&mut basename_ptr,
path_cstr.as_ptr(),
pool.as_mut_ptr(),
);
let dirname_str = std::ffi::CStr::from_ptr(dirname_ptr).to_str()?;
let basename_str = std::ffi::CStr::from_ptr(basename_ptr).to_str()?;
Ok((
Dirent(std::path::PathBuf::from(dirname_str)),
Dirent(std::path::PathBuf::from(basename_str)),
))
})
}
pub fn is_absolute_dirent(dirent: &Dirent) -> Result<bool, crate::Error<'_>> {
let path_cstr = std::ffi::CString::new(dirent.as_str())?;
unsafe {
let result = subversion_sys::svn_dirent_is_absolute(path_cstr.as_ptr());
Ok(result != 0)
}
}
pub fn is_root_dirent(dirent: &Dirent) -> Result<bool, crate::Error<'_>> {
let path_str = dirent.as_str();
let path_cstr = std::ffi::CString::new(path_str)?;
unsafe {
let result = subversion_sys::svn_dirent_is_root(path_cstr.as_ptr(), path_str.len());
Ok(result != 0)
}
}
pub fn is_canonical_dirent(dirent: &Dirent) -> Result<bool, crate::Error<'_>> {
with_tmp_pool(|pool| unsafe {
let path_cstr = std::ffi::CString::new(dirent.as_str())?;
let result = subversion_sys::svn_dirent_is_canonical(path_cstr.as_ptr(), pool.as_mut_ptr());
Ok(result != 0)
})
}
pub fn get_absolute_dirent(dirent: &Dirent) -> Result<Dirent, crate::Error<'_>> {
with_tmp_pool(|pool| unsafe {
let path_cstr = std::ffi::CString::new(dirent.as_str())?;
let mut absolute_ptr: *const std::ffi::c_char = std::ptr::null();
let err = subversion_sys::svn_dirent_get_absolute(
&mut absolute_ptr,
path_cstr.as_ptr(),
pool.as_mut_ptr(),
);
crate::Error::from_raw(err)?;
let absolute_cstr = std::ffi::CStr::from_ptr(absolute_ptr);
let absolute_str = absolute_cstr.to_str()?;
Ok(Dirent(std::path::PathBuf::from(absolute_str)))
})
}
pub fn to_absolute_cstring(
path: impl AsCanonicalDirent,
) -> Result<std::ffi::CString, crate::Error<'static>> {
let canonical = path.as_canonical_dirent()?;
with_tmp_pool(|pool| unsafe {
let path_cstr = std::ffi::CString::new(canonical.as_str())?;
let mut absolute_ptr: *const std::ffi::c_char = std::ptr::null();
let err = subversion_sys::svn_dirent_get_absolute(
&mut absolute_ptr,
path_cstr.as_ptr(),
pool.as_mut_ptr(),
);
crate::Error::from_raw(err)?;
let absolute_cstr = std::ffi::CStr::from_ptr(absolute_ptr);
Ok(std::ffi::CString::new(absolute_cstr.to_str()?)?)
})
}
pub fn canonicalize_path_or_url(input: &str) -> Result<std::ffi::CString, crate::Error<'static>> {
if crate::path::is_url(input) {
with_tmp_pool(|pool| unsafe {
let input_cstr = std::ffi::CString::new(input)?;
let canonical =
subversion_sys::svn_uri_canonicalize(input_cstr.as_ptr(), pool.as_mut_ptr());
let canonical_cstr = std::ffi::CStr::from_ptr(canonical);
Ok(std::ffi::CString::new(canonical_cstr.to_str()?)?)
})
} else {
to_absolute_cstring(input)
}
}
pub fn get_longest_ancestor(
dirent1: &Dirent,
dirent2: &Dirent,
) -> Result<Dirent, crate::Error<'static>> {
with_tmp_pool(|pool| unsafe {
let path1_cstr = std::ffi::CString::new(dirent1.as_str())?;
let path2_cstr = std::ffi::CString::new(dirent2.as_str())?;
let ancestor = subversion_sys::svn_dirent_get_longest_ancestor(
path1_cstr.as_ptr(),
path2_cstr.as_ptr(),
pool.as_mut_ptr(),
);
let ancestor_cstr = std::ffi::CStr::from_ptr(ancestor);
let ancestor_str = ancestor_cstr.to_str()?;
Ok(Dirent(std::path::PathBuf::from(ancestor_str)))
})
}
pub fn is_child(parent: &Dirent, child: &Dirent) -> Result<Option<Dirent>, crate::Error<'static>> {
with_tmp_pool(|pool| unsafe {
let parent_cstr = std::ffi::CString::new(parent.as_str())?;
let child_cstr = std::ffi::CString::new(child.as_str())?;
let result = subversion_sys::svn_dirent_is_child(
parent_cstr.as_ptr(),
child_cstr.as_ptr(),
pool.as_mut_ptr(),
);
if result.is_null() {
Ok(None)
} else {
let result_cstr = std::ffi::CStr::from_ptr(result);
let result_str = result_cstr.to_str()?;
Ok(Some(Dirent(std::path::PathBuf::from(result_str))))
}
})
}
pub fn is_ancestor(ancestor: &Dirent, path: &Dirent) -> Result<bool, crate::Error<'static>> {
let ancestor_cstr = std::ffi::CString::new(ancestor.as_str())?;
let path_cstr = std::ffi::CString::new(path.as_str())?;
unsafe {
let result =
subversion_sys::svn_dirent_is_ancestor(ancestor_cstr.as_ptr(), path_cstr.as_ptr());
Ok(result != 0)
}
}
pub fn skip_ancestor(
ancestor: &Dirent,
path: &Dirent,
) -> Result<Option<Dirent>, crate::Error<'static>> {
let ancestor_cstr = std::ffi::CString::new(ancestor.as_str())?;
let path_cstr = std::ffi::CString::new(path.as_str())?;
unsafe {
let result =
subversion_sys::svn_dirent_skip_ancestor(ancestor_cstr.as_ptr(), path_cstr.as_ptr());
if result.is_null() {
Ok(None)
} else {
let result_cstr = std::ffi::CStr::from_ptr(result);
let result_str = result_cstr.to_str()?;
Ok(Some(Dirent(std::path::PathBuf::from(result_str))))
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_join() {
let base = Dirent::new("/home/user").unwrap();
let result = base.join("project").unwrap();
assert_eq!(result.as_path(), std::path::Path::new("/home/user/project"));
let base = Dirent::new("/home/user/").unwrap();
let result = base.join("project").unwrap();
assert_eq!(result.as_path(), std::path::Path::new("/home/user/project"));
let base = Dirent::new("").unwrap();
let result = base.join("project").unwrap();
assert_eq!(result.as_path(), std::path::Path::new("project"));
}
#[test]
fn test_basename() {
let dirent = Dirent::new("/home/user/project").unwrap();
let result = dirent.basename().unwrap();
assert_eq!(result.as_path(), std::path::Path::new("project"));
let dirent = Dirent::new("/home/user/project/").unwrap();
let result = dirent.basename().unwrap();
assert_eq!(result.as_path(), std::path::Path::new("project"));
let dirent = Dirent::new("project").unwrap();
let result = dirent.basename().unwrap();
assert_eq!(result.as_path(), std::path::Path::new("project"));
}
#[test]
fn test_dirname() {
let dirent = Dirent::new("/home/user/project").unwrap();
let result = dirent.dirname().unwrap();
assert_eq!(result.as_path(), std::path::Path::new("/home/user"));
let dirent = Dirent::new("/home/user/project/").unwrap();
let result = dirent.dirname().unwrap();
assert_eq!(result.as_path(), std::path::Path::new("/home/user"));
let dirent = Dirent::new("project").unwrap();
let result = dirent.dirname().unwrap();
assert_eq!(result.as_path(), std::path::Path::new(""));
}
#[test]
fn test_split() {
let dirent = Dirent::new("/home/user/project").unwrap();
let (dirname, basename) = dirent.split().unwrap();
assert_eq!(dirname.as_path(), std::path::Path::new("/home/user"));
assert_eq!(basename.as_path(), std::path::Path::new("project"));
let dirent = Dirent::new("project").unwrap();
let (dirname, basename) = dirent.split().unwrap();
assert_eq!(dirname.as_path(), std::path::Path::new(""));
assert_eq!(basename.as_path(), std::path::Path::new("project"));
}
#[test]
#[cfg(unix)]
fn test_is_absolute() {
let dirent = Dirent::new("/home/user").unwrap();
assert!(dirent.is_absolute());
let dirent = Dirent::new("home/user").unwrap();
assert!(!dirent.is_absolute());
let dirent = Dirent::new("./home/user").unwrap();
assert!(!dirent.is_absolute());
let dirent = Dirent::new("../home/user").unwrap();
assert!(!dirent.is_absolute());
}
#[test]
fn test_is_root() {
let dirent = Dirent::new("/").unwrap();
assert!(dirent.is_root());
let dirent = Dirent::new("/home").unwrap();
assert!(!dirent.is_root());
let dirent = Dirent::new("home").unwrap();
assert!(!dirent.is_root());
let dirent = Dirent::new("").unwrap();
assert!(!dirent.is_root());
}
#[test]
fn test_is_canonical() {
let dirent = Dirent::new("/home/user").unwrap();
assert!(dirent.is_canonical());
let dirent = Dirent::new("/home/user/").unwrap();
assert!(dirent.is_canonical());
let dirent = Dirent::new("/home//user").unwrap();
assert!(dirent.is_canonical());
let dirent = Dirent::new("/home/./user").unwrap();
assert!(dirent.is_canonical());
}
#[test]
fn test_get_longest_ancestor() {
let dirent1 = Dirent::new("/home/user/project1").unwrap();
let dirent2 = Dirent::new("/home/user/project2").unwrap();
let result = get_longest_ancestor(&dirent1, &dirent2).unwrap();
assert_eq!(result.as_path(), std::path::Path::new("/home/user"));
let dirent1 = Dirent::new("/home/user").unwrap();
let dirent2 = Dirent::new("/var/log").unwrap();
let result = get_longest_ancestor(&dirent1, &dirent2).unwrap();
assert_eq!(result.as_path(), std::path::Path::new("/"));
let dirent1 = Dirent::new("project1").unwrap();
let dirent2 = Dirent::new("project2").unwrap();
let result = get_longest_ancestor(&dirent1, &dirent2).unwrap();
assert_eq!(result.as_path(), std::path::Path::new(""));
}
#[test]
fn test_is_child() {
let parent = Dirent::new("/home/user").unwrap();
let child = Dirent::new("/home/user/project").unwrap();
let result = is_child(&parent, &child).unwrap();
assert_eq!(
result.as_ref().map(|d| d.as_path()),
Some(std::path::Path::new("project"))
);
let parent = Dirent::new("/home/user").unwrap();
let child = Dirent::new("/var/log").unwrap();
let result = is_child(&parent, &child).unwrap();
assert_eq!(result, None);
let parent = Dirent::new("/home/user").unwrap();
let child = Dirent::new("/home/user").unwrap();
let result = is_child(&parent, &child).unwrap();
assert_eq!(result, None);
}
#[test]
fn test_is_ancestor() {
let ancestor = Dirent::new("/home").unwrap();
let path = Dirent::new("/home/user/project").unwrap();
assert!(is_ancestor(&ancestor, &path).unwrap());
let ancestor = Dirent::new("/home/user").unwrap();
let path = Dirent::new("/home/user/project").unwrap();
assert!(is_ancestor(&ancestor, &path).unwrap());
let ancestor = Dirent::new("/var").unwrap();
let path = Dirent::new("/home/user/project").unwrap();
assert!(!is_ancestor(&ancestor, &path).unwrap());
let ancestor = Dirent::new("/home/user/project").unwrap();
let path = Dirent::new("/home/user").unwrap();
assert!(!is_ancestor(&ancestor, &path).unwrap());
}
#[test]
fn test_skip_ancestor() {
let ancestor = Dirent::new("/home/user").unwrap();
let path = Dirent::new("/home/user/project/file.txt").unwrap();
let result = skip_ancestor(&ancestor, &path).unwrap();
assert_eq!(
result.as_ref().map(|d| d.as_path()),
Some(std::path::Path::new("project/file.txt"))
);
let ancestor = Dirent::new("/var/log").unwrap();
let path = Dirent::new("/home/user/project").unwrap();
let result = skip_ancestor(&ancestor, &path).unwrap();
assert_eq!(result, None);
let ancestor = Dirent::new("/home/user").unwrap();
let path = Dirent::new("/home/user").unwrap();
let result = skip_ancestor(&ancestor, &path).unwrap();
assert_eq!(
result.as_ref().map(|d| d.as_path()),
Some(std::path::Path::new(""))
);
}
#[test]
fn test_canonicalize_dirent() {
let result = canonicalize_dirent(std::path::Path::new("/home/user//project/")).unwrap();
assert_eq!(result, std::path::PathBuf::from("/home/user/project"));
let result = canonicalize_dirent(std::path::Path::new("")).unwrap();
assert_eq!(result, std::path::PathBuf::from(""));
}
#[test]
fn test_dirent_new_and_methods() {
let dirent = Dirent::new("/home/user//project/").unwrap();
assert_eq!(dirent.as_path(), std::path::Path::new("/home/user/project"));
assert_eq!(dirent.as_str(), "/home/user/project");
assert_eq!(dirent.to_string(), "/home/user/project");
}
#[test]
fn test_as_canonical_dirent_trait() {
let path_str = "/home/user/project";
let canonical = path_str.as_canonical_dirent().unwrap();
assert_eq!(
canonical.as_path(),
std::path::Path::new("/home/user/project")
);
let path_buf = std::path::PathBuf::from("/home/user/project");
let canonical = path_buf.as_canonical_dirent().unwrap();
assert_eq!(
canonical.as_path(),
std::path::Path::new("/home/user/project")
);
let path_ref = std::path::Path::new("/home/user/project");
let canonical = path_ref.as_canonical_dirent().unwrap();
assert_eq!(
canonical.as_path(),
std::path::Path::new("/home/user/project")
);
}
#[test]
fn test_invalid_inputs() {
let result = Dirent::new("path\0with\0nulls");
assert!(result.is_err(), "Should reject null bytes");
let dirent = Dirent::new("").unwrap();
assert_eq!(dirent.as_str(), "");
let dirent = Dirent::new("a").unwrap();
assert_eq!(dirent.as_str(), "a");
}
#[test]
#[cfg(unix)]
fn test_absolute_and_relative() {
let abs_dirent = Dirent::new("/home/user/project").unwrap();
assert!(abs_dirent.is_absolute());
assert!(!abs_dirent.is_root());
let rel_dirent = Dirent::new("project/subdir").unwrap();
assert!(!rel_dirent.is_absolute());
assert!(!rel_dirent.is_root());
let root_dirent = Dirent::new("/").unwrap();
assert!(root_dirent.is_absolute());
assert!(root_dirent.is_root());
let abs_base = Dirent::new("/home/user").unwrap();
let joined = abs_base.join("project").unwrap();
assert!(joined.is_absolute());
assert_eq!(joined.as_str(), "/home/user/project");
let rel_base = Dirent::new("home/user").unwrap();
let joined = rel_base.join("project").unwrap();
assert!(!joined.is_absolute());
assert_eq!(joined.as_str(), "home/user/project");
}
#[test]
#[cfg(unix)]
fn test_get_absolute() {
let rel_dirent = Dirent::new("project/subdir").unwrap();
assert!(!rel_dirent.is_absolute());
let abs_dirent = rel_dirent.get_absolute().unwrap();
assert!(abs_dirent.is_absolute());
assert!(abs_dirent.as_str().contains("project/subdir"));
let already_abs = Dirent::new("/home/user/project").unwrap();
assert!(already_abs.is_absolute());
let still_abs = already_abs.get_absolute().unwrap();
assert!(still_abs.is_absolute());
assert_eq!(already_abs.as_str(), still_abs.as_str());
}
#[test]
fn test_dirent_path_conversions() {
let original_path = std::path::Path::new("/home/user//project/");
let dirent = Dirent::new(original_path).unwrap();
assert_eq!(dirent.as_str(), "/home/user/project");
let path_ref: &std::path::Path = dirent.as_path();
assert_eq!(path_ref, std::path::Path::new("/home/user/project"));
let path_buf: std::path::PathBuf = dirent.as_path().to_path_buf();
assert_eq!(path_buf, std::path::PathBuf::from("/home/user/project"));
let path_ref: &std::path::Path = dirent.as_ref();
assert_eq!(path_ref, std::path::Path::new("/home/user/project"));
fn takes_path(p: &std::path::Path) -> String {
p.to_string_lossy().to_string()
}
let result = takes_path(dirent.as_ref());
assert_eq!(result, "/home/user/project");
let displayed = format!("{}", dirent);
assert_eq!(displayed, "/home/user/project");
}
#[test]
#[cfg(unix)]
fn test_special_paths() {
let dirent = Dirent::new("/").unwrap();
assert_eq!(dirent.basename().unwrap().as_str(), "");
assert_eq!(dirent.dirname().unwrap().as_str(), "/");
let (dir, base) = dirent.split().unwrap();
assert_eq!(dir.as_str(), "/");
assert_eq!(base.as_str(), "");
assert!(dirent.is_absolute());
assert!(dirent.is_root());
assert!(dirent.is_canonical());
let dirent = Dirent::new("").unwrap();
assert_eq!(dirent.basename().unwrap().as_str(), "");
assert_eq!(dirent.dirname().unwrap().as_str(), "");
let (dir, base) = dirent.split().unwrap();
assert_eq!(dir.as_str(), "");
assert_eq!(base.as_str(), "");
assert!(!dirent.is_absolute());
assert!(!dirent.is_root());
assert!(dirent.is_canonical());
let dirent = Dirent::new("single").unwrap();
assert_eq!(dirent.basename().unwrap().as_str(), "single");
assert_eq!(dirent.dirname().unwrap().as_str(), "");
let (dir, base) = dirent.split().unwrap();
assert_eq!(dir.as_str(), "");
assert_eq!(base.as_str(), "single");
assert!(!dirent.is_absolute());
assert!(!dirent.is_root());
assert!(dirent.is_canonical());
let dirent = Dirent::new("/single").unwrap();
assert_eq!(dirent.basename().unwrap().as_str(), "single");
assert_eq!(dirent.dirname().unwrap().as_str(), "/");
let (dir, base) = dirent.split().unwrap();
assert_eq!(dir.as_str(), "/");
assert_eq!(base.as_str(), "single");
assert!(dirent.is_absolute());
assert!(!dirent.is_root());
assert!(dirent.is_canonical());
let dirent = Dirent::new("a/b/c/d/e").unwrap();
assert_eq!(dirent.basename().unwrap().as_str(), "e");
assert_eq!(dirent.dirname().unwrap().as_str(), "a/b/c/d");
let (dir, base) = dirent.split().unwrap();
assert_eq!(dir.as_str(), "a/b/c/d");
assert_eq!(base.as_str(), "e");
assert!(!dirent.is_absolute());
assert!(!dirent.is_root());
assert!(dirent.is_canonical());
let dirent = Dirent::new("/a/b/c/d/e").unwrap();
assert_eq!(dirent.basename().unwrap().as_str(), "e");
assert_eq!(dirent.dirname().unwrap().as_str(), "/a/b/c/d");
let (dir, base) = dirent.split().unwrap();
assert_eq!(dir.as_str(), "/a/b/c/d");
assert_eq!(base.as_str(), "e");
assert!(dirent.is_absolute());
assert!(!dirent.is_root());
assert!(dirent.is_canonical());
}
#[test]
fn test_split_matches_basename_dirname() {
let test_cases = vec!["/home/user/project", "relative/path", "/", "", "single"];
for path in test_cases {
let dirent = Dirent::new(path).unwrap();
if let (Ok(basename), Ok(dirname)) = (dirent.basename(), dirent.dirname()) {
if let Ok((split_dirname, split_basename)) = dirent.split() {
assert_eq!(
basename.as_str(),
split_basename.as_str(),
"basename() and split() basename should match for path: {}",
path
);
assert_eq!(
dirname.as_str(),
split_dirname.as_str(),
"dirname() and split() dirname should match for path: {}",
path
);
}
}
let joined = dirent.join("test").unwrap();
assert!(
joined.as_str().contains("test"),
"Join should include component"
);
}
}
#[test]
#[cfg(unix)]
fn test_to_file_url() {
let dirent = Dirent::new("/tmp").unwrap();
let url = dirent.to_file_url().unwrap();
assert_eq!(url.as_str(), "file:///tmp");
}
#[test]
#[cfg(unix)]
fn test_to_file_url_root() {
let dirent = Dirent::new("/").unwrap();
let url = dirent.to_file_url().unwrap();
assert_eq!(url.as_str(), "file://");
}
#[test]
#[cfg(unix)]
fn test_to_file_url_nested() {
let dirent = Dirent::new("/home/user/project").unwrap();
let url = dirent.to_file_url().unwrap();
assert_eq!(url.as_str(), "file:///home/user/project");
}
}