pub struct VirtualPath<Marker = ()> { /* private fields */ }
Expand description
SUMMARY:
Hold a user‑facing path clamped to a virtual root ("/"
) over a PathBoundary
.
DETAILS:
virtualpath_display()
shows rooted, forward‑slashed paths (e.g., "/a/b.txt"
).
Use virtual manipulation methods to compose paths while preserving clamping, then convert to
StrictPath
with unvirtual()
for system‑facing I/O.
Implementations§
Source§impl<Marker> VirtualPath<Marker>
impl<Marker> VirtualPath<Marker>
Sourcepub fn with_root<P: AsRef<Path>>(root: P) -> Result<Self>
pub fn with_root<P: AsRef<Path>>(root: P) -> Result<Self>
SUMMARY:
Create the virtual root ("/"
) for the given filesystem root.
Sourcepub fn with_root_create<P: AsRef<Path>>(root: P) -> Result<Self>
pub fn with_root_create<P: AsRef<Path>>(root: P) -> Result<Self>
SUMMARY: Create the virtual root, creating the filesystem root if missing.
Sourcepub fn unvirtual(self) -> StrictPath<Marker>
pub fn unvirtual(self) -> StrictPath<Marker>
SUMMARY:
Convert this VirtualPath
back into a system‑facing StrictPath
.
Sourcepub fn change_marker<NewMarker>(self) -> VirtualPath<NewMarker>
pub fn change_marker<NewMarker>(self) -> VirtualPath<NewMarker>
SUMMARY: Change the compile-time marker while keeping the virtual and strict views in sync.
WHEN TO USE:
- After authenticating/authorizing a user and granting them access to a virtual path
- When escalating or downgrading permissions (e.g., ReadOnly → ReadWrite)
- When reinterpreting a path’s domain (e.g., TempStorage → UserUploads)
WHEN NOT TO USE:
- When converting between path types - conversions preserve markers automatically
- When the current marker already matches your needs - no transformation needed
- When you haven’t verified authorization - NEVER change markers without checking permissions
PARAMETERS:
_none_
RETURNS:
VirtualPath<NewMarker>
: Same clamped path encoded with the new marker.
ERRORS:
_none_
SECURITY: This method performs no permission checks. Only elevate markers after verifying real authorization out-of-band.
EXAMPLE:
// Simulated authorization: verify user credentials before granting access
fn grant_user_access(user_token: &str, path: VirtualPath<GuestAccess>) -> Option<VirtualPath<UserAccess>> {
if user_token == "valid-token-12345" {
Some(path.change_marker()) // ✅ Only after token validation
} else {
None // ❌ Invalid token
}
}
let guest_path: VirtualPath<GuestAccess> = guest_root.virtual_join("docs/readme.md")?;
let user_path = grant_user_access("valid-token-12345", guest_path).expect("authorized");
assert_eq!(user_path.virtualpath_display().to_string(), "/docs/readme.md");
Type Safety Guarantee:
The following code fails to compile because you cannot pass a path with one marker type to a function expecting a different marker type. This compile-time check enforces that permission changes are explicit and cannot be bypassed accidentally.
fn require_editor(_: VirtualPath<EditorAccess>) {}
let guest_file = guest_root.virtual_join("docs/manual.txt").unwrap();
// ❌ Compile error: expected `VirtualPath<EditorAccess>`, found `VirtualPath<GuestAccess>`
require_editor(guest_file);
Sourcepub fn try_into_root(self) -> Result<VirtualRoot<Marker>>
pub fn try_into_root(self) -> Result<VirtualRoot<Marker>>
SUMMARY:
Consume and return the VirtualRoot
for its boundary (no directory creation).
RETURNS:
Result<VirtualRoot<Marker>>
: Virtual root anchored at the strict path’s directory.
ERRORS:
StrictPathError::InvalidRestriction
: Propagated fromtry_into_boundary
when the strict path does not exist or is not a directory.
Sourcepub fn try_into_root_create(self) -> Result<VirtualRoot<Marker>>
pub fn try_into_root_create(self) -> Result<VirtualRoot<Marker>>
SUMMARY:
Consume and return a VirtualRoot
, creating the underlying directory if missing.
RETURNS:
Result<VirtualRoot<Marker>>
: Virtual root anchored at the strict path’s directory (created if necessary).
ERRORS:
StrictPathError::InvalidRestriction
: Propagated fromtry_into_boundary
or directory creation failures wrapped inInvalidRestriction
.
Sourcepub fn as_unvirtual(&self) -> &StrictPath<Marker>
pub fn as_unvirtual(&self) -> &StrictPath<Marker>
SUMMARY:
Borrow the underlying system‑facing StrictPath
(no allocation).
Sourcepub fn interop_path(&self) -> &OsStr
pub fn interop_path(&self) -> &OsStr
SUMMARY:
Return the underlying system path as &OsStr
for unavoidable third-party AsRef<Path>
interop.
Sourcepub fn virtual_join<P: AsRef<Path>>(&self, path: P) -> Result<Self>
pub fn virtual_join<P: AsRef<Path>>(&self, path: P) -> Result<Self>
SUMMARY: Join a virtual path segment (virtual semantics) and re‑validate within the same restriction.
DETAILS:
Applies virtual path clamping: absolute paths are interpreted relative to the virtual root,
and traversal attempts are clamped to prevent escaping the boundary. This method maintains
the security guarantee that all VirtualPath
instances stay within their virtual root.
PARAMETERS:
path
(impl AsRef<Path>
): Path segment to join. Absolute paths are clamped to virtual root.
RETURNS:
Result<VirtualPath<Marker>>
: New virtual path within the same restriction.
EXAMPLE:
let vroot: VirtualRoot = VirtualRoot::try_new_create(td.path())?;
let base = vroot.virtual_join("data")?;
// Absolute paths are clamped to virtual root
let abs = base.virtual_join("/etc/config")?;
assert_eq!(abs.virtualpath_display().to_string(), "/etc/config");
Sourcepub fn virtualpath_parent(&self) -> Result<Option<Self>>
pub fn virtualpath_parent(&self) -> Result<Option<Self>>
SUMMARY:
Return the parent virtual path, or None
at the virtual root.
Sourcepub fn virtualpath_with_file_name<S: AsRef<OsStr>>(
&self,
file_name: S,
) -> Result<Self>
pub fn virtualpath_with_file_name<S: AsRef<OsStr>>( &self, file_name: S, ) -> Result<Self>
SUMMARY: Return a new virtual path with file name changed, preserving clamping.
Sourcepub fn virtualpath_with_extension<S: AsRef<OsStr>>(
&self,
extension: S,
) -> Result<Self>
pub fn virtualpath_with_extension<S: AsRef<OsStr>>( &self, extension: S, ) -> Result<Self>
SUMMARY: Return a new virtual path with the extension changed, preserving clamping.
Sourcepub fn virtualpath_file_name(&self) -> Option<&OsStr>
pub fn virtualpath_file_name(&self) -> Option<&OsStr>
SUMMARY: Return the file name component of the virtual path, if any.
Sourcepub fn virtualpath_file_stem(&self) -> Option<&OsStr>
pub fn virtualpath_file_stem(&self) -> Option<&OsStr>
SUMMARY: Return the file stem of the virtual path, if any.
Sourcepub fn virtualpath_extension(&self) -> Option<&OsStr>
pub fn virtualpath_extension(&self) -> Option<&OsStr>
SUMMARY: Return the extension of the virtual path, if any.
Sourcepub fn virtualpath_starts_with<P: AsRef<Path>>(&self, p: P) -> bool
pub fn virtualpath_starts_with<P: AsRef<Path>>(&self, p: P) -> bool
SUMMARY:
Return true
if the virtual path starts with the given prefix (virtual semantics).
Sourcepub fn virtualpath_ends_with<P: AsRef<Path>>(&self, p: P) -> bool
pub fn virtualpath_ends_with<P: AsRef<Path>>(&self, p: P) -> bool
SUMMARY:
Return true
if the virtual path ends with the given suffix (virtual semantics).
Sourcepub fn virtualpath_display(&self) -> VirtualPathDisplay<'_, Marker>
pub fn virtualpath_display(&self) -> VirtualPathDisplay<'_, Marker>
SUMMARY: Return a Display wrapper that shows a rooted virtual path (e.g., `“/a/b.txt”).
Sourcepub fn is_dir(&self) -> bool
pub fn is_dir(&self) -> bool
SUMMARY:
Return true
if the underlying system path is a directory.
Sourcepub fn metadata(&self) -> Result<Metadata>
pub fn metadata(&self) -> Result<Metadata>
SUMMARY: Return metadata for the underlying system path.
Sourcepub fn read_to_string(&self) -> Result<String>
pub fn read_to_string(&self) -> Result<String>
SUMMARY:
Read the file contents as String
from the underlying system path.
Sourcepub fn read_bytes(&self) -> Result<Vec<u8>>
👎Deprecated since 0.1.0-alpha.5: Use read() instead
pub fn read_bytes(&self) -> Result<Vec<u8>>
Reads the file contents as raw bytes from the underlying system path.
Sourcepub fn write_bytes(&self, data: &[u8]) -> Result<()>
👎Deprecated since 0.1.0-alpha.5: Use write(…) instead
pub fn write_bytes(&self, data: &[u8]) -> Result<()>
Writes raw bytes to the underlying system path.
Sourcepub fn write_string(&self, data: &str) -> Result<()>
👎Deprecated since 0.1.0-alpha.5: Use write(…) instead
pub fn write_string(&self, data: &str) -> Result<()>
Writes a UTF-8 string to the underlying system path.
Sourcepub fn read(&self) -> Result<Vec<u8>>
pub fn read(&self) -> Result<Vec<u8>>
SUMMARY:
Read raw bytes from the underlying system path (replacement for read_bytes
).
Sourcepub fn read_dir(&self) -> Result<ReadDir>
pub fn read_dir(&self) -> Result<ReadDir>
SUMMARY:
Read directory entries (discovery). Re‑join names with virtual_join(...)
to preserve clamping.
Sourcepub fn write<C: AsRef<[u8]>>(&self, contents: C) -> Result<()>
pub fn write<C: AsRef<[u8]>>(&self, contents: C) -> Result<()>
SUMMARY:
Write bytes to the underlying system path. Accepts &str
, String
, &[u8]
, Vec<u8]
, etc.
Sourcepub fn create_file(&self) -> Result<File>
pub fn create_file(&self) -> Result<File>
SUMMARY: Create or truncate the file at this virtual path and return a writable handle.
PARAMETERS:
- none
RETURNS:
std::fs::File
: Writable handle scoped to the same virtual root restriction.
ERRORS:
std::io::Error
: Propagates operating-system errors when the parent directory is missing or file creation fails.
EXAMPLE:
let report = vroot.virtual_join("reports/summary.txt")?;
report.create_parent_dir_all()?;
let mut file = report.create_file()?;
file.write_all(b"summary")?;
Sourcepub fn open_file(&self) -> Result<File>
pub fn open_file(&self) -> Result<File>
SUMMARY: Open the file at this virtual path in read-only mode.
PARAMETERS:
- none
RETURNS:
std::fs::File
: Read-only handle scoped to the same virtual root restriction.
ERRORS:
std::io::Error
: Propagates operating-system errors when the file is missing or inaccessible.
EXAMPLE:
let report = vroot.virtual_join("reports/summary.txt")?;
report.create_parent_dir_all()?;
report.write("summary")?;
let mut file = report.open_file()?;
let mut contents = String::new();
file.read_to_string(&mut contents)?;
assert_eq!(contents, "summary");
Sourcepub fn create_dir_all(&self) -> Result<()>
pub fn create_dir_all(&self) -> Result<()>
SUMMARY: Create all directories in the underlying system path if missing.
Sourcepub fn create_dir(&self) -> Result<()>
pub fn create_dir(&self) -> Result<()>
SUMMARY: Create the directory at this virtual location (non‑recursive). Fails if parent missing.
Sourcepub fn create_parent_dir(&self) -> Result<()>
pub fn create_parent_dir(&self) -> Result<()>
SUMMARY:
Create only the immediate parent of this virtual path (non‑recursive). Ok(())
at virtual root.
Sourcepub fn create_parent_dir_all(&self) -> Result<()>
pub fn create_parent_dir_all(&self) -> Result<()>
SUMMARY:
Recursively create all missing directories up to the immediate parent. Ok(())
at virtual root.
Sourcepub fn remove_file(&self) -> Result<()>
pub fn remove_file(&self) -> Result<()>
SUMMARY: Remove the file at the underlying system path.
Sourcepub fn remove_dir(&self) -> Result<()>
pub fn remove_dir(&self) -> Result<()>
SUMMARY: Remove the directory at the underlying system path.
Sourcepub fn remove_dir_all(&self) -> Result<()>
pub fn remove_dir_all(&self) -> Result<()>
SUMMARY: Recursively remove the directory and its contents at the underlying system path.
Sourcepub fn virtual_symlink(&self, link_path: &Self) -> Result<()>
pub fn virtual_symlink(&self, link_path: &Self) -> Result<()>
SUMMARY:
Create a symlink at link_path
pointing to this virtual path (same virtual root required).
DETAILS:
Both self
(target) and link_path
must be VirtualPath
instances created via virtual_join()
,
which ensures all paths are clamped to the virtual root. Absolute paths like "/etc/config"
passed to virtual_join()
are automatically clamped to vroot/etc/config
, ensuring symlinks
cannot escape the virtual root boundary.
EXAMPLE:
let vroot: VirtualRoot = VirtualRoot::try_new_create(td.path())?;
// Create target - absolute path "/etc/config" is clamped to vroot/etc/config
let target = vroot.virtual_join("/etc/config/app.conf")?;
target.create_parent_dir_all()?;
target.write(b"config data")?;
// Create symlink - absolute path "/var/app/link" is clamped to vroot/var/app/link
let link = vroot.virtual_join("/var/app/link.conf")?;
link.create_parent_dir_all()?;
// On Windows, symlink creation may fail without privileges - gracefully handle
if let Err(e) = target.virtual_symlink(&link) {
// Windows ERROR_PRIVILEGE_NOT_HELD (1314) is expected without admin/dev mode
#[cfg(windows)]
if e.raw_os_error() == Some(1314) { return Ok(()); }
return Err(e.into());
}
assert_eq!(link.read_to_string()?, "config data");
Sourcepub fn virtual_hard_link(&self, link_path: &Self) -> Result<()>
pub fn virtual_hard_link(&self, link_path: &Self) -> Result<()>
SUMMARY:
Create a hard link at link_path
pointing to this virtual path (same virtual root required).
DETAILS:
Both self
(target) and link_path
must be VirtualPath
instances created via virtual_join()
,
which ensures all paths are clamped to the virtual root. Absolute paths like "/etc/data"
passed to virtual_join()
are automatically clamped to vroot/etc/data
, ensuring hard links
cannot escape the virtual root boundary.
EXAMPLE:
let vroot: VirtualRoot = VirtualRoot::try_new_create(td.path())?;
// Create target - absolute path clamped to virtual root
let target = vroot.virtual_join("/shared/data.dat")?;
target.create_parent_dir_all()?;
target.write(b"shared data")?;
// Create hard link - also clamped to virtual root
let link = vroot.virtual_join("/backup/data.dat")?;
link.create_parent_dir_all()?;
target.virtual_hard_link(&link)?;
// Modify through link, verify through target (hard link behavior)
link.write(b"modified")?;
assert_eq!(target.read_to_string()?, "modified");
Sourcepub fn virtual_rename<P: AsRef<Path>>(&self, dest: P) -> Result<()>
pub fn virtual_rename<P: AsRef<Path>>(&self, dest: P) -> Result<()>
SUMMARY: Rename/move within the same virtual root. Relative destinations are siblings; absolute are clamped to root.
DETAILS:
Accepts impl AsRef<Path>
for the destination. Absolute paths (starting with "/"
) are
automatically clamped to the virtual root via internal virtual_join()
call, ensuring the
destination cannot escape the virtual boundary. Relative paths are resolved as siblings.
Parent directories are not created automatically.
PARAMETERS:
dest
(impl AsRef<Path>
): Destination path. Absolute paths like"/archive/file.txt"
are clamped tovroot/archive/file.txt
.
EXAMPLE:
let vroot: VirtualRoot = VirtualRoot::try_new_create(td.path())?;
let source = vroot.virtual_join("temp/file.txt")?;
source.create_parent_dir_all()?;
source.write(b"content")?;
// Absolute destination path is clamped to virtual root
let dest_dir = vroot.virtual_join("/archive")?;
dest_dir.create_dir_all()?;
source.virtual_rename("/archive/file.txt")?;
let renamed = vroot.virtual_join("/archive/file.txt")?;
assert_eq!(renamed.read_to_string()?, "content");
Sourcepub fn virtual_copy<P: AsRef<Path>>(&self, dest: P) -> Result<u64>
pub fn virtual_copy<P: AsRef<Path>>(&self, dest: P) -> Result<u64>
SUMMARY: Copy within the same virtual root. Relative destinations are siblings; absolute are clamped to root.
DETAILS:
Accepts impl AsRef<Path>
for the destination. Absolute paths (starting with "/"
) are
automatically clamped to the virtual root via internal virtual_join()
call, ensuring the
destination cannot escape the virtual boundary. Relative paths are resolved as siblings.
Parent directories are not created automatically. Returns the number of bytes copied.
PARAMETERS:
dest
(impl AsRef<Path>
): Destination path. Absolute paths like"/backup/file.txt"
are clamped tovroot/backup/file.txt
.
RETURNS:
u64
: Number of bytes copied.
EXAMPLE:
let vroot: VirtualRoot = VirtualRoot::try_new_create(td.path())?;
let source = vroot.virtual_join("data/source.txt")?;
source.create_parent_dir_all()?;
source.write(b"data to copy")?;
// Absolute destination path is clamped to virtual root
let dest_dir = vroot.virtual_join("/backup")?;
dest_dir.create_dir_all()?;
let bytes = source.virtual_copy("/backup/copy.txt")?;
let copied = vroot.virtual_join("/backup/copy.txt")?;
assert_eq!(copied.read_to_string()?, "data to copy");
assert_eq!(bytes, 12);
Trait Implementations§
Source§impl<Marker: Clone> Clone for VirtualPath<Marker>
impl<Marker: Clone> Clone for VirtualPath<Marker>
Source§fn clone(&self) -> VirtualPath<Marker>
fn clone(&self) -> VirtualPath<Marker>
1.0.0 · Source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
source
. Read moreSource§impl<Marker> Debug for VirtualPath<Marker>
impl<Marker> Debug for VirtualPath<Marker>
Source§impl<Marker> Hash for VirtualPath<Marker>
impl<Marker> Hash for VirtualPath<Marker>
Source§impl<Marker> Ord for VirtualPath<Marker>
impl<Marker> Ord for VirtualPath<Marker>
Source§impl<Marker> PartialEq<StrictPath<Marker>> for VirtualPath<Marker>
impl<Marker> PartialEq<StrictPath<Marker>> for VirtualPath<Marker>
Source§impl<Marker> PartialEq<VirtualPath<Marker>> for StrictPath<Marker>
Available on crate feature virtual-path
only.
impl<Marker> PartialEq<VirtualPath<Marker>> for StrictPath<Marker>
virtual-path
only.Source§impl<Marker> PartialEq for VirtualPath<Marker>
impl<Marker> PartialEq for VirtualPath<Marker>
Source§impl<T: AsRef<Path>, Marker> PartialOrd<T> for VirtualPath<Marker>
impl<T: AsRef<Path>, Marker> PartialOrd<T> for VirtualPath<Marker>
Source§impl<Marker> PartialOrd for VirtualPath<Marker>
impl<Marker> PartialOrd for VirtualPath<Marker>
Source§impl<Marker> Serialize for VirtualPath<Marker>
Available on crate feature serde
only.
impl<Marker> Serialize for VirtualPath<Marker>
serde
only.