-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathnormalize.rs
More file actions
150 lines (138 loc) · 3.98 KB
/
Copy pathnormalize.rs
File metadata and controls
150 lines (138 loc) · 3.98 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
//! Separator and escape normalisation for glob patterns.
/// Normalise forward slashes in a glob `pattern` to the platform separator.
///
/// Forward slashes are rewritten to [`std::path::MAIN_SEPARATOR`] so manifests
/// can use `/` portably. On Unix, backslash escapes are preserved for the
/// follow-up literal-escape pass rather than treated as separators here.
pub(crate) fn normalize_separators(pattern: &str) -> String {
let native = std::path::MAIN_SEPARATOR;
#[cfg(unix)]
{
let mut out = String::with_capacity(pattern.len());
let mut it = pattern.chars().peekable();
while let Some(c) = it.next() {
if c == '\\' {
push_normalized_backslash(&mut it, &mut out, native);
continue;
}
if c == '/' {
out.push(native);
continue;
}
out.push(c);
}
out
}
#[cfg(not(unix))]
{
pattern.replace('/', &native.to_string())
}
}
#[cfg(unix)]
fn push_normalized_backslash(
it: &mut std::iter::Peekable<std::str::Chars<'_>>,
out: &mut String,
native: char,
) {
let Some(next) = it.peek().copied() else {
out.push('\\');
return;
};
if matches!(next, '[' | ']' | '{' | '}') {
out.push('\\');
return;
}
if matches!(next, '*' | '?') {
let mut lookahead = it.clone();
lookahead.next();
let should_preserve_wildcard = match lookahead.peek() {
None => true,
Some(&ch) => is_wildcard_continuation_char(ch),
};
if should_preserve_wildcard {
out.push('\\');
return;
}
}
out.push(native);
}
#[cfg(not(unix))]
#[expect(
dead_code,
reason = "stub keeps cfg alignment; unused on non-Unix targets"
)]
fn push_normalized_backslash(
_it: &mut std::iter::Peekable<std::str::Chars<'_>>,
out: &mut String,
_native: char,
) {
// On non-Unix targets the function is never invoked because the
// normalisation path uses the `replace` branch. Provide a stub to keep
// cfg alignment in sync for future callers.
out.push('\\');
}
/// Rewrite selected backslash escapes into bracket character classes.
///
/// The `glob` crate treats `\` literally on Unix, so an escaped metacharacter
/// such as `\*` is converted to the single-element class `[*]` to match the
/// literal character. Only escapes for `*`, `?`, `[`, `]`, `{`, and `}` are
/// rewritten; other escapes are left for the `glob` crate to interpret.
#[cfg(unix)]
pub(super) fn force_literal_escapes(pattern: &str) -> String {
let mut out = String::with_capacity(pattern.len());
let mut it = pattern.chars().peekable();
let mut in_class = false;
while let Some(c) = it.next() {
match c {
'[' if !in_class => {
in_class = true;
out.push(c);
}
']' if in_class => {
in_class = false;
out.push(c);
}
'\\' if !in_class => handle_escaped_char(&mut it, &mut out),
_ => out.push(c),
}
}
out
}
#[cfg(unix)]
fn handle_escaped_char(it: &mut std::iter::Peekable<std::str::Chars<'_>>, out: &mut String) {
let Some(next) = it.peek().copied() else {
out.push('\\');
return;
};
match next {
'*' => {
it.next();
out.push_str("[*]");
}
'?' => {
it.next();
out.push_str("[?]");
}
'[' => {
it.next();
out.push_str("[[]");
}
']' => {
it.next();
out.push_str("[]]");
}
'{' => {
it.next();
out.push_str("[{]");
}
'}' => {
it.next();
out.push_str("[}]");
}
_ => out.push('\\'),
}
}
#[cfg(unix)]
fn is_wildcard_continuation_char(ch: char) -> bool {
ch.is_alphanumeric() || ch == '-' || ch == '_'
}