Add Windows specific path parsing (#11119)
Since Windows paths are known to be weird and currently not handled at all (outside of relative paths that just happen to work), I figured I would add a windows specific implementation for parsing absolute paths. It should be functionally the same, of course there's always a chance I missed an edge case though. This should fix - #10849 Note that there are still some cases that will probably break the current implementation, namely local drives that do not have a drive letter assigned (not sure how to handle those). There's also UNC paths but I don't know how important those are at the moment (I'll allow myself to assume not at all) Release Notes: - N/A
This commit is contained in:
parent
11bc28080f
commit
9edd81c740
1 changed files with 108 additions and 1 deletions
|
@ -189,7 +189,17 @@ impl<P> PathLikeWithPosition<P> {
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
|
|
||||||
match s.trim().split_once(FILE_ROW_COLUMN_DELIMITER) {
|
let trimmed = s.trim();
|
||||||
|
|
||||||
|
#[cfg(target_os = "windows")]
|
||||||
|
{
|
||||||
|
let is_absolute = trimmed.starts_with(r"\\?\");
|
||||||
|
if is_absolute {
|
||||||
|
return Self::parse_absolute_path(trimmed, parse_path_like_str);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
match trimmed.split_once(FILE_ROW_COLUMN_DELIMITER) {
|
||||||
Some((path_like_str, maybe_row_and_col_str)) => {
|
Some((path_like_str, maybe_row_and_col_str)) => {
|
||||||
let path_like_str = path_like_str.trim();
|
let path_like_str = path_like_str.trim();
|
||||||
let maybe_row_and_col_str = maybe_row_and_col_str.trim();
|
let maybe_row_and_col_str = maybe_row_and_col_str.trim();
|
||||||
|
@ -243,6 +253,58 @@ impl<P> PathLikeWithPosition<P> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// This helper function is used for parsing absolute paths on Windows. It exists because absolute paths on Windows are quite different from other platforms. See [this page](https://learn.microsoft.com/en-us/dotnet/standard/io/file-path-formats#dos-device-paths) for more information.
|
||||||
|
#[cfg(target_os = "windows")]
|
||||||
|
fn parse_absolute_path<E>(
|
||||||
|
s: &str,
|
||||||
|
parse_path_like_str: impl Fn(&str) -> Result<P, E>,
|
||||||
|
) -> Result<Self, E> {
|
||||||
|
let fallback = |fallback_str| {
|
||||||
|
Ok(Self {
|
||||||
|
path_like: parse_path_like_str(fallback_str)?,
|
||||||
|
row: None,
|
||||||
|
column: None,
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut iterator = s.split(FILE_ROW_COLUMN_DELIMITER);
|
||||||
|
|
||||||
|
let drive_prefix = iterator.next().unwrap_or_default();
|
||||||
|
let file_path = iterator.next().unwrap_or_default();
|
||||||
|
|
||||||
|
// TODO: How to handle drives without a letter? UNC paths?
|
||||||
|
let complete_path = drive_prefix.replace("\\\\?\\", "") + ":" + &file_path;
|
||||||
|
|
||||||
|
if let Some(row_str) = iterator.next() {
|
||||||
|
if let Some(column_str) = iterator.next() {
|
||||||
|
match row_str.parse::<u32>() {
|
||||||
|
Ok(row) => match column_str.parse::<u32>() {
|
||||||
|
Ok(col) => {
|
||||||
|
return Ok(Self {
|
||||||
|
path_like: parse_path_like_str(&complete_path)?,
|
||||||
|
row: Some(row),
|
||||||
|
column: Some(col),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Err(_) => {
|
||||||
|
return Ok(Self {
|
||||||
|
path_like: parse_path_like_str(&complete_path)?,
|
||||||
|
row: Some(row),
|
||||||
|
column: None,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
Err(_) => {
|
||||||
|
return fallback(&complete_path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return fallback(&complete_path);
|
||||||
|
}
|
||||||
|
|
||||||
pub fn map_path_like<P2, E>(
|
pub fn map_path_like<P2, E>(
|
||||||
self,
|
self,
|
||||||
mapping: impl FnOnce(P) -> Result<P2, E>,
|
mapping: impl FnOnce(P) -> Result<P2, E>,
|
||||||
|
@ -392,6 +454,7 @@ mod tests {
|
||||||
// Trim off trailing `:`s for otherwise valid input.
|
// Trim off trailing `:`s for otherwise valid input.
|
||||||
#[test]
|
#[test]
|
||||||
fn path_with_position_parsing_special() {
|
fn path_with_position_parsing_special() {
|
||||||
|
#[cfg(not(target_os = "windows"))]
|
||||||
let input_and_expected = [
|
let input_and_expected = [
|
||||||
(
|
(
|
||||||
"test_file.rs:",
|
"test_file.rs:",
|
||||||
|
@ -419,6 +482,50 @@ mod tests {
|
||||||
),
|
),
|
||||||
];
|
];
|
||||||
|
|
||||||
|
#[cfg(target_os = "windows")]
|
||||||
|
let input_and_expected = [
|
||||||
|
(
|
||||||
|
"test_file.rs:",
|
||||||
|
PathLikeWithPosition {
|
||||||
|
path_like: "test_file.rs".to_string(),
|
||||||
|
row: None,
|
||||||
|
column: None,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"test_file.rs:1:",
|
||||||
|
PathLikeWithPosition {
|
||||||
|
path_like: "test_file.rs".to_string(),
|
||||||
|
row: Some(1),
|
||||||
|
column: None,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"\\\\?\\C:\\Users\\someone\\test_file.rs:1902:13:",
|
||||||
|
PathLikeWithPosition {
|
||||||
|
path_like: "C:\\Users\\someone\\test_file.rs".to_string(),
|
||||||
|
row: Some(1902),
|
||||||
|
column: Some(13),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"\\\\?\\C:\\Users\\someone\\test_file.rs:1902:13:15:",
|
||||||
|
PathLikeWithPosition {
|
||||||
|
path_like: "C:\\Users\\someone\\test_file.rs".to_string(),
|
||||||
|
row: Some(1902),
|
||||||
|
column: Some(13),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"\\\\?\\C:\\Users\\someone\\test_file.rs:1902:::15:",
|
||||||
|
PathLikeWithPosition {
|
||||||
|
path_like: "C:\\Users\\someone\\test_file.rs".to_string(),
|
||||||
|
row: Some(1902),
|
||||||
|
column: None,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
];
|
||||||
|
|
||||||
for (input, expected) in input_and_expected {
|
for (input, expected) in input_and_expected {
|
||||||
let actual = parse_str(input);
|
let actual = parse_str(input);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue