mirror of
https://github.com/openai/codex.git
synced 2026-10-18 17:18:52 +00:00
Infer Windows UNC paths with forward and mixed slashes (#49424)
## Why `LegacyAppPathString` treated `//server/share/project` as POSIX, losing its UNC interpretation when inferring a `PathUri`. ## What changed Infer Windows syntax whenever an API path starts with two separators, including forward and mixed slashes, independently of the host. For example, `//server/share/project` now converts to `file://server/share/project`. Double-slash POSIX paths require an explicit `PathConvention::Posix`; inferred paths still undergo validation. ## Testing Add regression tests for UNC and namespace paths, mixed separators, URI serialization round trips, explicit POSIX conversion, convention mismatches, and malformed UNC paths. GitOrigin-RevId: 720893de517addb7a938e6784d1ed0f05df3ec0d
This commit is contained in:
committed by
copyberry
parent
ab84d71f57
commit
d1d0e89558
@@ -162,9 +162,11 @@ impl LegacyAppPathString {
|
||||
|
||||
/// Infers the path convention of an absolute API path from its spelling.
|
||||
///
|
||||
/// Relative paths and ambiguous spellings return `None`. In particular,
|
||||
/// slash-prefixed paths are treated as POSIX even when they could also be
|
||||
/// interpreted as slash-delimited Windows UNC paths.
|
||||
/// Two leading separators select Windows UNC or namespace syntax, including
|
||||
/// forward and mixed slashes, independently of the current host. This favors
|
||||
/// UNC paths over ambiguous double-slash POSIX paths. Call [`Self::to_path_uri`]
|
||||
/// with an explicit POSIX convention to preserve that interpretation.
|
||||
/// Relative paths return `None`; inferred prefixes still require validation.
|
||||
pub fn infer_absolute_path_convention(&self) -> Option<PathConvention> {
|
||||
let bytes = self.0.as_bytes();
|
||||
let has_windows_drive_root = matches!(
|
||||
@@ -172,7 +174,12 @@ impl LegacyAppPathString {
|
||||
[drive, b':', separator, ..]
|
||||
if drive.is_ascii_alphabetic() && is_windows_separator_byte(*separator)
|
||||
);
|
||||
if has_windows_drive_root || self.0.starts_with(r"\\") {
|
||||
let has_windows_unc_root = matches!(
|
||||
bytes,
|
||||
[first, second, ..]
|
||||
if is_windows_separator_byte(*first) && is_windows_separator_byte(*second)
|
||||
);
|
||||
if has_windows_drive_root || has_windows_unc_root {
|
||||
Some(PathConvention::Windows)
|
||||
} else if self.0.starts_with('/') {
|
||||
Some(PathConvention::Posix)
|
||||
|
||||
@@ -418,7 +418,7 @@ fn infers_absolute_path_conventions_from_api_text() {
|
||||
(r"\\.\COM1", Some(PathConvention::Windows)),
|
||||
("/workspace/file.rs", Some(PathConvention::Posix)),
|
||||
("/C:/workspace/file.rs", Some(PathConvention::Posix)),
|
||||
("//server/share/file.rs", Some(PathConvention::Posix)),
|
||||
("//server/share/file.rs", Some(PathConvention::Windows)),
|
||||
("", None),
|
||||
(".", None),
|
||||
("subdir/file.rs", None),
|
||||
@@ -657,3 +657,62 @@ fn serializes_and_deserializes_as_a_string() {
|
||||
rendered
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn inferred_unc_paths_accept_forward_and_mixed_slashes_on_every_host() {
|
||||
for raw in [
|
||||
"//server/share/project",
|
||||
r"//server/share\project",
|
||||
r"/\server/share/project",
|
||||
r"\/server/share/project",
|
||||
r"\\server\share\project",
|
||||
"//wsl.localhost/Ubuntu/home/user/project",
|
||||
"//127.0.0.1/c$/project",
|
||||
"//LOCALHOST/c$/project",
|
||||
"//?/C:/project",
|
||||
] {
|
||||
let path = LegacyAppPathString::from_string(raw);
|
||||
let expected = path.to_path_uri(PathConvention::Windows).expect(raw);
|
||||
let inferred = path.to_inferred_path_uri().expect(raw);
|
||||
assert_eq!(
|
||||
(inferred.clone(), inferred.infer_path_convention()),
|
||||
(expected, Some(PathConvention::Windows)),
|
||||
"{raw}"
|
||||
);
|
||||
let serialized = serde_json::to_string(&inferred).expect("serialize URI");
|
||||
assert_eq!(
|
||||
serde_json::from_str::<PathUri>(&serialized).expect("deserialize URI"),
|
||||
inferred,
|
||||
"{raw}"
|
||||
);
|
||||
}
|
||||
let slash = LegacyAppPathString::from_string("//server/share/project")
|
||||
.to_inferred_path_uri()
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
slash,
|
||||
PathUri::parse("file://server/share/project").unwrap()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn double_slash_posix_paths_require_an_explicit_convention() {
|
||||
let path = LegacyAppPathString::from_string("//server/share/project");
|
||||
assert_eq!(
|
||||
path.to_path_uri(PathConvention::Posix).unwrap(),
|
||||
PathUri::parse("file:///server/share/project").unwrap()
|
||||
);
|
||||
let cwd = PathUri::parse("file:///workspace").unwrap();
|
||||
assert!(matches!(
|
||||
path.resolve_against(&cwd, /*user_home_dir*/ None),
|
||||
Err(LegacyAppPathStringError::MismatchedConvention { .. })
|
||||
));
|
||||
for raw in ["//", "//server", "//server/", "///server/share"] {
|
||||
assert!(
|
||||
LegacyAppPathString::from_string(raw)
|
||||
.to_inferred_path_uri()
|
||||
.is_none(),
|
||||
"{raw}"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user