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