##// END OF EJS Templates
rust-filepatterns: use literal b'#' instead of cast
Yuya Nishihara -
r42858:30f8e786 default
parent child Browse files
Show More
@@ -1,371 +1,371 b''
1 use crate::{
1 use crate::{
2 utils::{files::get_path_from_bytes, replace_slice, SliceExt},
2 utils::{files::get_path_from_bytes, replace_slice, SliceExt},
3 LineNumber, PatternError, PatternFileError,
3 LineNumber, PatternError, PatternFileError,
4 };
4 };
5 use lazy_static::lazy_static;
5 use lazy_static::lazy_static;
6 use regex::bytes::Regex;
6 use regex::bytes::Regex;
7 use std::collections::HashMap;
7 use std::collections::HashMap;
8 use std::fs::File;
8 use std::fs::File;
9 use std::io::Read;
9 use std::io::Read;
10 use std::vec::Vec;
10 use std::vec::Vec;
11
11
12 lazy_static! {
12 lazy_static! {
13 static ref RE_ESCAPE: Vec<Vec<u8>> = {
13 static ref RE_ESCAPE: Vec<Vec<u8>> = {
14 let mut v: Vec<Vec<u8>> = (0..=255).map(|byte| vec![byte]).collect();
14 let mut v: Vec<Vec<u8>> = (0..=255).map(|byte| vec![byte]).collect();
15 let to_escape = b"()[]{}?*+-|^$\\.&~# \t\n\r\x0b\x0c";
15 let to_escape = b"()[]{}?*+-|^$\\.&~# \t\n\r\x0b\x0c";
16 for byte in to_escape {
16 for byte in to_escape {
17 v[*byte as usize].insert(0, b'\\');
17 v[*byte as usize].insert(0, b'\\');
18 }
18 }
19 v
19 v
20 };
20 };
21 }
21 }
22
22
23 /// These are matched in order
23 /// These are matched in order
24 const GLOB_REPLACEMENTS: &[(&[u8], &[u8])] =
24 const GLOB_REPLACEMENTS: &[(&[u8], &[u8])] =
25 &[(b"*/", b"(?:.*/)?"), (b"*", b".*"), (b"", b"[^/]*")];
25 &[(b"*/", b"(?:.*/)?"), (b"*", b".*"), (b"", b"[^/]*")];
26
26
27 #[derive(Debug, Copy, Clone, PartialEq, Eq)]
27 #[derive(Debug, Copy, Clone, PartialEq, Eq)]
28 pub enum PatternSyntax {
28 pub enum PatternSyntax {
29 Regexp,
29 Regexp,
30 /// Glob that matches at the front of the path
30 /// Glob that matches at the front of the path
31 RootGlob,
31 RootGlob,
32 /// Glob that matches at any suffix of the path (still anchored at slashes)
32 /// Glob that matches at any suffix of the path (still anchored at slashes)
33 Glob,
33 Glob,
34 Path,
34 Path,
35 RelPath,
35 RelPath,
36 RelGlob,
36 RelGlob,
37 RelRegexp,
37 RelRegexp,
38 RootFiles,
38 RootFiles,
39 }
39 }
40
40
41 /// Transforms a glob pattern into a regex
41 /// Transforms a glob pattern into a regex
42 fn glob_to_re(pat: &[u8]) -> Vec<u8> {
42 fn glob_to_re(pat: &[u8]) -> Vec<u8> {
43 let mut input = pat;
43 let mut input = pat;
44 let mut res: Vec<u8> = vec![];
44 let mut res: Vec<u8> = vec![];
45 let mut group_depth = 0;
45 let mut group_depth = 0;
46
46
47 while let Some((c, rest)) = input.split_first() {
47 while let Some((c, rest)) = input.split_first() {
48 input = rest;
48 input = rest;
49
49
50 match c {
50 match c {
51 b'*' => {
51 b'*' => {
52 for (source, repl) in GLOB_REPLACEMENTS {
52 for (source, repl) in GLOB_REPLACEMENTS {
53 if input.starts_with(source) {
53 if input.starts_with(source) {
54 input = &input[source.len()..];
54 input = &input[source.len()..];
55 res.extend(*repl);
55 res.extend(*repl);
56 break;
56 break;
57 }
57 }
58 }
58 }
59 }
59 }
60 b'?' => res.extend(b"."),
60 b'?' => res.extend(b"."),
61 b'[' => {
61 b'[' => {
62 match input.iter().skip(1).position(|b| *b == b']') {
62 match input.iter().skip(1).position(|b| *b == b']') {
63 None => res.extend(b"\\["),
63 None => res.extend(b"\\["),
64 Some(end) => {
64 Some(end) => {
65 // Account for the one we skipped
65 // Account for the one we skipped
66 let end = end + 1;
66 let end = end + 1;
67
67
68 res.extend(b"[");
68 res.extend(b"[");
69
69
70 for (i, b) in input[..end].iter().enumerate() {
70 for (i, b) in input[..end].iter().enumerate() {
71 if *b == b'!' && i == 0 {
71 if *b == b'!' && i == 0 {
72 res.extend(b"^")
72 res.extend(b"^")
73 } else if *b == b'^' && i == 0 {
73 } else if *b == b'^' && i == 0 {
74 res.extend(b"\\^")
74 res.extend(b"\\^")
75 } else if *b == b'\\' {
75 } else if *b == b'\\' {
76 res.extend(b"\\\\")
76 res.extend(b"\\\\")
77 } else {
77 } else {
78 res.push(*b)
78 res.push(*b)
79 }
79 }
80 }
80 }
81 res.extend(b"]");
81 res.extend(b"]");
82 input = &input[end + 1..];
82 input = &input[end + 1..];
83 }
83 }
84 }
84 }
85 }
85 }
86 b'{' => {
86 b'{' => {
87 group_depth += 1;
87 group_depth += 1;
88 res.extend(b"(?:")
88 res.extend(b"(?:")
89 }
89 }
90 b'}' if group_depth > 0 => {
90 b'}' if group_depth > 0 => {
91 group_depth -= 1;
91 group_depth -= 1;
92 res.extend(b")");
92 res.extend(b")");
93 }
93 }
94 b',' if group_depth > 0 => res.extend(b"|"),
94 b',' if group_depth > 0 => res.extend(b"|"),
95 b'\\' => {
95 b'\\' => {
96 let c = {
96 let c = {
97 if let Some((c, rest)) = input.split_first() {
97 if let Some((c, rest)) = input.split_first() {
98 input = rest;
98 input = rest;
99 c
99 c
100 } else {
100 } else {
101 c
101 c
102 }
102 }
103 };
103 };
104 res.extend(&RE_ESCAPE[*c as usize])
104 res.extend(&RE_ESCAPE[*c as usize])
105 }
105 }
106 _ => res.extend(&RE_ESCAPE[*c as usize]),
106 _ => res.extend(&RE_ESCAPE[*c as usize]),
107 }
107 }
108 }
108 }
109 res
109 res
110 }
110 }
111
111
112 fn escape_pattern(pattern: &[u8]) -> Vec<u8> {
112 fn escape_pattern(pattern: &[u8]) -> Vec<u8> {
113 pattern
113 pattern
114 .iter()
114 .iter()
115 .flat_map(|c| RE_ESCAPE[*c as usize].clone())
115 .flat_map(|c| RE_ESCAPE[*c as usize].clone())
116 .collect()
116 .collect()
117 }
117 }
118
118
119 fn parse_pattern_syntax(kind: &[u8]) -> Result<PatternSyntax, PatternError> {
119 fn parse_pattern_syntax(kind: &[u8]) -> Result<PatternSyntax, PatternError> {
120 match kind {
120 match kind {
121 b"re" => Ok(PatternSyntax::Regexp),
121 b"re" => Ok(PatternSyntax::Regexp),
122 b"path" => Ok(PatternSyntax::Path),
122 b"path" => Ok(PatternSyntax::Path),
123 b"relpath" => Ok(PatternSyntax::RelPath),
123 b"relpath" => Ok(PatternSyntax::RelPath),
124 b"rootfilesin" => Ok(PatternSyntax::RootFiles),
124 b"rootfilesin" => Ok(PatternSyntax::RootFiles),
125 b"relglob" => Ok(PatternSyntax::RelGlob),
125 b"relglob" => Ok(PatternSyntax::RelGlob),
126 b"relre" => Ok(PatternSyntax::RelRegexp),
126 b"relre" => Ok(PatternSyntax::RelRegexp),
127 b"glob" => Ok(PatternSyntax::Glob),
127 b"glob" => Ok(PatternSyntax::Glob),
128 b"rootglob" => Ok(PatternSyntax::RootGlob),
128 b"rootglob" => Ok(PatternSyntax::RootGlob),
129 _ => Err(PatternError::UnsupportedSyntax(
129 _ => Err(PatternError::UnsupportedSyntax(
130 String::from_utf8_lossy(kind).to_string(),
130 String::from_utf8_lossy(kind).to_string(),
131 )),
131 )),
132 }
132 }
133 }
133 }
134
134
135 /// Builds the regex that corresponds to the given pattern.
135 /// Builds the regex that corresponds to the given pattern.
136 /// If within a `syntax: regexp` context, returns the pattern,
136 /// If within a `syntax: regexp` context, returns the pattern,
137 /// otherwise, returns the corresponding regex.
137 /// otherwise, returns the corresponding regex.
138 fn _build_single_regex(
138 fn _build_single_regex(
139 syntax: PatternSyntax,
139 syntax: PatternSyntax,
140 pattern: &[u8],
140 pattern: &[u8],
141 globsuffix: &[u8],
141 globsuffix: &[u8],
142 ) -> Vec<u8> {
142 ) -> Vec<u8> {
143 if pattern.is_empty() {
143 if pattern.is_empty() {
144 return vec![];
144 return vec![];
145 }
145 }
146 match syntax {
146 match syntax {
147 PatternSyntax::Regexp => pattern.to_owned(),
147 PatternSyntax::Regexp => pattern.to_owned(),
148 PatternSyntax::RelRegexp => {
148 PatternSyntax::RelRegexp => {
149 if pattern[0] == b'^' {
149 if pattern[0] == b'^' {
150 return pattern.to_owned();
150 return pattern.to_owned();
151 }
151 }
152 let mut res = b".*".to_vec();
152 let mut res = b".*".to_vec();
153 res.extend(pattern);
153 res.extend(pattern);
154 res
154 res
155 }
155 }
156 PatternSyntax::Path | PatternSyntax::RelPath => {
156 PatternSyntax::Path | PatternSyntax::RelPath => {
157 if pattern == b"." {
157 if pattern == b"." {
158 return vec![];
158 return vec![];
159 }
159 }
160 let mut pattern = escape_pattern(pattern);
160 let mut pattern = escape_pattern(pattern);
161 pattern.extend(b"(?:/|$)");
161 pattern.extend(b"(?:/|$)");
162 pattern
162 pattern
163 }
163 }
164 PatternSyntax::RootFiles => {
164 PatternSyntax::RootFiles => {
165 let mut res = if pattern == b"." {
165 let mut res = if pattern == b"." {
166 vec![]
166 vec![]
167 } else {
167 } else {
168 // Pattern is a directory name.
168 // Pattern is a directory name.
169 let mut as_vec: Vec<u8> = escape_pattern(pattern);
169 let mut as_vec: Vec<u8> = escape_pattern(pattern);
170 as_vec.push(b'/');
170 as_vec.push(b'/');
171 as_vec
171 as_vec
172 };
172 };
173
173
174 // Anything after the pattern must be a non-directory.
174 // Anything after the pattern must be a non-directory.
175 res.extend(b"[^/]+$");
175 res.extend(b"[^/]+$");
176 res
176 res
177 }
177 }
178 PatternSyntax::Glob
178 PatternSyntax::Glob
179 | PatternSyntax::RelGlob
179 | PatternSyntax::RelGlob
180 | PatternSyntax::RootGlob => {
180 | PatternSyntax::RootGlob => {
181 let mut res: Vec<u8> = vec![];
181 let mut res: Vec<u8> = vec![];
182 if syntax == PatternSyntax::RelGlob {
182 if syntax == PatternSyntax::RelGlob {
183 res.extend(b"(?:|.*/)");
183 res.extend(b"(?:|.*/)");
184 }
184 }
185
185
186 res.extend(glob_to_re(pattern));
186 res.extend(glob_to_re(pattern));
187 res.extend(globsuffix.iter());
187 res.extend(globsuffix.iter());
188 res
188 res
189 }
189 }
190 }
190 }
191 }
191 }
192
192
193 const GLOB_SPECIAL_CHARACTERS: [u8; 7] =
193 const GLOB_SPECIAL_CHARACTERS: [u8; 7] =
194 [b'*', b'?', b'[', b']', b'{', b'}', b'\\'];
194 [b'*', b'?', b'[', b']', b'{', b'}', b'\\'];
195
195
196 /// Wrapper function to `_build_single_regex` that short-circuits 'exact' globs
196 /// Wrapper function to `_build_single_regex` that short-circuits 'exact' globs
197 /// that don't need to be transformed into a regex.
197 /// that don't need to be transformed into a regex.
198 pub fn build_single_regex(
198 pub fn build_single_regex(
199 kind: &[u8],
199 kind: &[u8],
200 pat: &[u8],
200 pat: &[u8],
201 globsuffix: &[u8],
201 globsuffix: &[u8],
202 ) -> Result<Vec<u8>, PatternError> {
202 ) -> Result<Vec<u8>, PatternError> {
203 let enum_kind = parse_pattern_syntax(kind)?;
203 let enum_kind = parse_pattern_syntax(kind)?;
204 if enum_kind == PatternSyntax::RootGlob
204 if enum_kind == PatternSyntax::RootGlob
205 && !pat.iter().any(|b| GLOB_SPECIAL_CHARACTERS.contains(b))
205 && !pat.iter().any(|b| GLOB_SPECIAL_CHARACTERS.contains(b))
206 {
206 {
207 let mut escaped = escape_pattern(pat);
207 let mut escaped = escape_pattern(pat);
208 escaped.extend(b"(?:/|$)");
208 escaped.extend(b"(?:/|$)");
209 Ok(escaped)
209 Ok(escaped)
210 } else {
210 } else {
211 Ok(_build_single_regex(enum_kind, pat, globsuffix))
211 Ok(_build_single_regex(enum_kind, pat, globsuffix))
212 }
212 }
213 }
213 }
214
214
215 lazy_static! {
215 lazy_static! {
216 static ref SYNTAXES: HashMap<&'static [u8], &'static [u8]> = {
216 static ref SYNTAXES: HashMap<&'static [u8], &'static [u8]> = {
217 let mut m = HashMap::new();
217 let mut m = HashMap::new();
218
218
219 m.insert(b"re".as_ref(), b"relre:".as_ref());
219 m.insert(b"re".as_ref(), b"relre:".as_ref());
220 m.insert(b"regexp".as_ref(), b"relre:".as_ref());
220 m.insert(b"regexp".as_ref(), b"relre:".as_ref());
221 m.insert(b"glob".as_ref(), b"relglob:".as_ref());
221 m.insert(b"glob".as_ref(), b"relglob:".as_ref());
222 m.insert(b"rootglob".as_ref(), b"rootglob:".as_ref());
222 m.insert(b"rootglob".as_ref(), b"rootglob:".as_ref());
223 m.insert(b"include".as_ref(), b"include".as_ref());
223 m.insert(b"include".as_ref(), b"include".as_ref());
224 m.insert(b"subinclude".as_ref(), b"subinclude".as_ref());
224 m.insert(b"subinclude".as_ref(), b"subinclude".as_ref());
225 m
225 m
226 };
226 };
227 }
227 }
228
228
229 pub type PatternTuple = (Vec<u8>, LineNumber, Vec<u8>);
229 pub type PatternTuple = (Vec<u8>, LineNumber, Vec<u8>);
230 type WarningTuple = (Vec<u8>, Vec<u8>);
230 type WarningTuple = (Vec<u8>, Vec<u8>);
231
231
232 pub fn parse_pattern_file_contents(
232 pub fn parse_pattern_file_contents(
233 lines: &[u8],
233 lines: &[u8],
234 file_path: &[u8],
234 file_path: &[u8],
235 warn: bool,
235 warn: bool,
236 ) -> (Vec<PatternTuple>, Vec<WarningTuple>) {
236 ) -> (Vec<PatternTuple>, Vec<WarningTuple>) {
237 let comment_regex = Regex::new(r"((?:^|[^\\])(?:\\\\)*)#.*").unwrap();
237 let comment_regex = Regex::new(r"((?:^|[^\\])(?:\\\\)*)#.*").unwrap();
238 let mut inputs: Vec<PatternTuple> = vec![];
238 let mut inputs: Vec<PatternTuple> = vec![];
239 let mut warnings: Vec<WarningTuple> = vec![];
239 let mut warnings: Vec<WarningTuple> = vec![];
240
240
241 let mut current_syntax = b"relre:".as_ref();
241 let mut current_syntax = b"relre:".as_ref();
242
242
243 for (line_number, mut line) in lines.split(|c| *c == b'\n').enumerate() {
243 for (line_number, mut line) in lines.split(|c| *c == b'\n').enumerate() {
244 let line_number = line_number + 1;
244 let line_number = line_number + 1;
245
245
246 if line.contains(&('#' as u8)) {
246 if line.contains(&b'#') {
247 if let Some(cap) = comment_regex.captures(line) {
247 if let Some(cap) = comment_regex.captures(line) {
248 line = &line[..cap.get(1).unwrap().end()]
248 line = &line[..cap.get(1).unwrap().end()]
249 }
249 }
250 let mut line = line.to_owned();
250 let mut line = line.to_owned();
251 replace_slice(&mut line, br"\#", b"#");
251 replace_slice(&mut line, br"\#", b"#");
252 }
252 }
253
253
254 let mut line = line.trim_end();
254 let mut line = line.trim_end();
255
255
256 if line.is_empty() {
256 if line.is_empty() {
257 continue;
257 continue;
258 }
258 }
259
259
260 if line.starts_with(b"syntax:") {
260 if line.starts_with(b"syntax:") {
261 let syntax = line[b"syntax:".len()..].trim();
261 let syntax = line[b"syntax:".len()..].trim();
262
262
263 if let Some(rel_syntax) = SYNTAXES.get(syntax) {
263 if let Some(rel_syntax) = SYNTAXES.get(syntax) {
264 current_syntax = rel_syntax;
264 current_syntax = rel_syntax;
265 } else if warn {
265 } else if warn {
266 warnings.push((file_path.to_owned(), syntax.to_owned()));
266 warnings.push((file_path.to_owned(), syntax.to_owned()));
267 }
267 }
268 continue;
268 continue;
269 }
269 }
270
270
271 let mut line_syntax: &[u8] = &current_syntax;
271 let mut line_syntax: &[u8] = &current_syntax;
272
272
273 for (s, rels) in SYNTAXES.iter() {
273 for (s, rels) in SYNTAXES.iter() {
274 if line.starts_with(rels) {
274 if line.starts_with(rels) {
275 line_syntax = rels;
275 line_syntax = rels;
276 line = &line[rels.len()..];
276 line = &line[rels.len()..];
277 break;
277 break;
278 } else if line.starts_with(&[s, b":".as_ref()].concat()) {
278 } else if line.starts_with(&[s, b":".as_ref()].concat()) {
279 line_syntax = rels;
279 line_syntax = rels;
280 line = &line[s.len() + 1..];
280 line = &line[s.len() + 1..];
281 break;
281 break;
282 }
282 }
283 }
283 }
284
284
285 inputs.push((
285 inputs.push((
286 [line_syntax, line].concat(),
286 [line_syntax, line].concat(),
287 line_number,
287 line_number,
288 line.to_owned(),
288 line.to_owned(),
289 ));
289 ));
290 }
290 }
291 (inputs, warnings)
291 (inputs, warnings)
292 }
292 }
293
293
294 pub fn read_pattern_file(
294 pub fn read_pattern_file(
295 file_path: &[u8],
295 file_path: &[u8],
296 warn: bool,
296 warn: bool,
297 ) -> Result<(Vec<PatternTuple>, Vec<WarningTuple>), PatternFileError> {
297 ) -> Result<(Vec<PatternTuple>, Vec<WarningTuple>), PatternFileError> {
298 let mut f = File::open(get_path_from_bytes(file_path))?;
298 let mut f = File::open(get_path_from_bytes(file_path))?;
299 let mut contents = Vec::new();
299 let mut contents = Vec::new();
300
300
301 f.read_to_end(&mut contents)?;
301 f.read_to_end(&mut contents)?;
302
302
303 Ok(parse_pattern_file_contents(&contents, file_path, warn))
303 Ok(parse_pattern_file_contents(&contents, file_path, warn))
304 }
304 }
305
305
306 #[cfg(test)]
306 #[cfg(test)]
307 mod tests {
307 mod tests {
308 use super::*;
308 use super::*;
309
309
310 #[test]
310 #[test]
311 fn escape_pattern_test() {
311 fn escape_pattern_test() {
312 let untouched = br#"!"%',/0123456789:;<=>@ABCDEFGHIJKLMNOPQRSTUVWXYZ_`abcdefghijklmnopqrstuvwxyz"#;
312 let untouched = br#"!"%',/0123456789:;<=>@ABCDEFGHIJKLMNOPQRSTUVWXYZ_`abcdefghijklmnopqrstuvwxyz"#;
313 assert_eq!(escape_pattern(untouched), untouched.to_vec());
313 assert_eq!(escape_pattern(untouched), untouched.to_vec());
314 // All escape codes
314 // All escape codes
315 assert_eq!(
315 assert_eq!(
316 escape_pattern(br#"()[]{}?*+-|^$\\.&~# \t\n\r\v\f"#),
316 escape_pattern(br#"()[]{}?*+-|^$\\.&~# \t\n\r\v\f"#),
317 br#"\(\)\[\]\{\}\?\*\+\-\|\^\$\\\\\.\&\~\#\ \\t\\n\\r\\v\\f"#
317 br#"\(\)\[\]\{\}\?\*\+\-\|\^\$\\\\\.\&\~\#\ \\t\\n\\r\\v\\f"#
318 .to_vec()
318 .to_vec()
319 );
319 );
320 }
320 }
321
321
322 #[test]
322 #[test]
323 fn glob_test() {
323 fn glob_test() {
324 assert_eq!(glob_to_re(br#"?"#), br#"."#);
324 assert_eq!(glob_to_re(br#"?"#), br#"."#);
325 assert_eq!(glob_to_re(br#"*"#), br#"[^/]*"#);
325 assert_eq!(glob_to_re(br#"*"#), br#"[^/]*"#);
326 assert_eq!(glob_to_re(br#"**"#), br#".*"#);
326 assert_eq!(glob_to_re(br#"**"#), br#".*"#);
327 assert_eq!(glob_to_re(br#"**/a"#), br#"(?:.*/)?a"#);
327 assert_eq!(glob_to_re(br#"**/a"#), br#"(?:.*/)?a"#);
328 assert_eq!(glob_to_re(br#"a/**/b"#), br#"a/(?:.*/)?b"#);
328 assert_eq!(glob_to_re(br#"a/**/b"#), br#"a/(?:.*/)?b"#);
329 assert_eq!(glob_to_re(br#"[a*?!^][^b][!c]"#), br#"[a*?!^][\^b][^c]"#);
329 assert_eq!(glob_to_re(br#"[a*?!^][^b][!c]"#), br#"[a*?!^][\^b][^c]"#);
330 assert_eq!(glob_to_re(br#"{a,b}"#), br#"(?:a|b)"#);
330 assert_eq!(glob_to_re(br#"{a,b}"#), br#"(?:a|b)"#);
331 assert_eq!(glob_to_re(br#".\*\?"#), br#"\.\*\?"#);
331 assert_eq!(glob_to_re(br#".\*\?"#), br#"\.\*\?"#);
332 }
332 }
333
333
334 #[test]
334 #[test]
335 fn test_parse_pattern_file_contents() {
335 fn test_parse_pattern_file_contents() {
336 let lines = b"syntax: glob\n*.elc";
336 let lines = b"syntax: glob\n*.elc";
337
337
338 assert_eq!(
338 assert_eq!(
339 vec![(b"relglob:*.elc".to_vec(), 2, b"*.elc".to_vec())],
339 vec![(b"relglob:*.elc".to_vec(), 2, b"*.elc".to_vec())],
340 parse_pattern_file_contents(lines, b"file_path", false).0,
340 parse_pattern_file_contents(lines, b"file_path", false).0,
341 );
341 );
342
342
343 let lines = b"syntax: include\nsyntax: glob";
343 let lines = b"syntax: include\nsyntax: glob";
344
344
345 assert_eq!(
345 assert_eq!(
346 parse_pattern_file_contents(lines, b"file_path", false).0,
346 parse_pattern_file_contents(lines, b"file_path", false).0,
347 vec![]
347 vec![]
348 );
348 );
349 let lines = b"glob:**.o";
349 let lines = b"glob:**.o";
350 assert_eq!(
350 assert_eq!(
351 parse_pattern_file_contents(lines, b"file_path", false).0,
351 parse_pattern_file_contents(lines, b"file_path", false).0,
352 vec![(b"relglob:**.o".to_vec(), 1, b"**.o".to_vec())]
352 vec![(b"relglob:**.o".to_vec(), 1, b"**.o".to_vec())]
353 );
353 );
354 }
354 }
355
355
356 #[test]
356 #[test]
357 fn test_build_single_regex_shortcut() {
357 fn test_build_single_regex_shortcut() {
358 assert_eq!(
358 assert_eq!(
359 br"(?:/|$)".to_vec(),
359 br"(?:/|$)".to_vec(),
360 build_single_regex(b"rootglob", b"", b"").unwrap()
360 build_single_regex(b"rootglob", b"", b"").unwrap()
361 );
361 );
362 assert_eq!(
362 assert_eq!(
363 br"whatever(?:/|$)".to_vec(),
363 br"whatever(?:/|$)".to_vec(),
364 build_single_regex(b"rootglob", b"whatever", b"").unwrap()
364 build_single_regex(b"rootglob", b"whatever", b"").unwrap()
365 );
365 );
366 assert_eq!(
366 assert_eq!(
367 br"[^/]*\.o".to_vec(),
367 br"[^/]*\.o".to_vec(),
368 build_single_regex(b"rootglob", b"*.o", b"").unwrap()
368 build_single_regex(b"rootglob", b"*.o", b"").unwrap()
369 );
369 );
370 }
370 }
371 }
371 }
General Comments 0
You need to be logged in to leave comments. Login now