Fix "invalid path" error for UNC paths on Windows

resolve_path() only handled Prefix::Disk and Prefix::VerbatimDisk when
initializing the path root on Windows, so any UNC path (\server\share,
or its \?\UNC\server\share verbatim form) fell through to the
catch-all branch and errored with "invalid path", even though the path
itself was perfectly valid. This broke zoxide entirely for anyone whose
directories live on a network share.

Add a Prefix::UNC / Prefix::VerbatimUNC arm that builds the \server\share
root the same way the Disk arms build their drive root, and add
resolve_path unit tests covering UNC, verbatim UNC, and a disk-path
regression check.

Fixes #331
This commit is contained in:
hamodywe 2026-08-09 01:08:28 +03:00
parent 6d6066dd25
commit 55bc9f0f2f
1 changed files with 45 additions and 0 deletions

View File

@ -332,6 +332,18 @@ pub fn resolve_path(path: impl AsRef<Path>) -> Result<PathBuf> {
base_path = get_drive_path(drive_letter);
stack.extend(base_path.components());
}
Prefix::UNC(server, share) | Prefix::VerbatimUNC(server, share) => {
let server = server.to_string_lossy();
let share = share.to_string_lossy();
components.next();
if components.peek() == Some(&Component::RootDir) {
components.next();
}
base_path = format!(r"\\{server}\{share}").into();
stack.extend(base_path.components());
}
_ => bail!("invalid path: {}", path.display()),
},
Some(Component::RootDir) => {
@ -378,3 +390,36 @@ pub fn to_lowercase(s: impl AsRef<str>) -> String {
let s = s.as_ref();
if s.is_ascii() { s.to_ascii_lowercase() } else { s.to_lowercase() }
}
#[cfg(all(test, windows))]
mod tests {
use super::*;
#[test]
fn resolve_path_unc() {
let resolved = resolve_path(r"\\a.network-drive\a-folder").unwrap();
assert_eq!(resolved, PathBuf::from(r"\\a.network-drive\a-folder"));
}
#[test]
fn resolve_path_unc_with_subdirs() {
let resolved = resolve_path(r"\\a.network-drive\a-folder\subdir\nested").unwrap();
assert_eq!(resolved, PathBuf::from(r"\\a.network-drive\a-folder\subdir\nested"));
}
#[test]
fn resolve_path_verbatim_unc() {
// `\\?\UNC\server\share\...` is the verbatim form Windows APIs produce
// for UNC paths (e.g. via `dunce`/`canonicalize` on paths that were
// already long-path-prefixed). It should resolve to the same
// non-verbatim form as a plain UNC path.
let resolved = resolve_path(r"\\?\UNC\a.network-drive\a-folder\subdir").unwrap();
assert_eq!(resolved, PathBuf::from(r"\\a.network-drive\a-folder\subdir"));
}
#[test]
fn resolve_path_disk() {
let resolved = resolve_path(r"C:\Users\test").unwrap();
assert_eq!(resolved, PathBuf::from(r"C:\Users\test"));
}
}