##// END OF EJS Templates
rustfilepatterns: shorter code for concatenating slices...
Valentin Gatien-Baron -
r43133:69195b6f default
parent child Browse files
Show More
@@ -1,391 +1,377 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 | PatternSyntax::RootGlob => {
190 PatternSyntax::Glob | PatternSyntax::RootGlob => {
202 let mut res: Vec<u8> = vec![];
191 [glob_to_re(pattern).as_slice(), globsuffix].concat()
203 res.extend(glob_to_re(pattern));
204 res.extend(globsuffix.iter());
205 res
206 }
192 }
207 }
193 }
208 }
194 }
209
195
210 const GLOB_SPECIAL_CHARACTERS: [u8; 7] =
196 const GLOB_SPECIAL_CHARACTERS: [u8; 7] =
211 [b'*', b'?', b'[', b']', b'{', b'}', b'\\'];
197 [b'*', b'?', b'[', b']', b'{', b'}', b'\\'];
212
198
213 /// Wrapper function to `_build_single_regex` that short-circuits 'exact' globs
199 /// Wrapper function to `_build_single_regex` that short-circuits 'exact' globs
214 /// that don't need to be transformed into a regex.
200 /// that don't need to be transformed into a regex.
215 pub fn build_single_regex(
201 pub fn build_single_regex(
216 kind: &[u8],
202 kind: &[u8],
217 pat: &[u8],
203 pat: &[u8],
218 globsuffix: &[u8],
204 globsuffix: &[u8],
219 ) -> Result<Vec<u8>, PatternError> {
205 ) -> Result<Vec<u8>, PatternError> {
220 let enum_kind = parse_pattern_syntax(kind)?;
206 let enum_kind = parse_pattern_syntax(kind)?;
221 if enum_kind == PatternSyntax::RootGlob
207 if enum_kind == PatternSyntax::RootGlob
222 && !pat.iter().any(|b| GLOB_SPECIAL_CHARACTERS.contains(b))
208 && !pat.iter().any(|b| GLOB_SPECIAL_CHARACTERS.contains(b))
223 {
209 {
224 let mut escaped = escape_pattern(pat);
210 let mut escaped = escape_pattern(pat);
225 escaped.extend(b"(?:/|$)");
211 escaped.extend(b"(?:/|$)");
226 Ok(escaped)
212 Ok(escaped)
227 } else {
213 } else {
228 Ok(_build_single_regex(enum_kind, pat, globsuffix))
214 Ok(_build_single_regex(enum_kind, pat, globsuffix))
229 }
215 }
230 }
216 }
231
217
232 lazy_static! {
218 lazy_static! {
233 static ref SYNTAXES: HashMap<&'static [u8], &'static [u8]> = {
219 static ref SYNTAXES: HashMap<&'static [u8], &'static [u8]> = {
234 let mut m = HashMap::new();
220 let mut m = HashMap::new();
235
221
236 m.insert(b"re".as_ref(), b"relre:".as_ref());
222 m.insert(b"re".as_ref(), b"relre:".as_ref());
237 m.insert(b"regexp".as_ref(), b"relre:".as_ref());
223 m.insert(b"regexp".as_ref(), b"relre:".as_ref());
238 m.insert(b"glob".as_ref(), b"relglob:".as_ref());
224 m.insert(b"glob".as_ref(), b"relglob:".as_ref());
239 m.insert(b"rootglob".as_ref(), b"rootglob:".as_ref());
225 m.insert(b"rootglob".as_ref(), b"rootglob:".as_ref());
240 m.insert(b"include".as_ref(), b"include".as_ref());
226 m.insert(b"include".as_ref(), b"include".as_ref());
241 m.insert(b"subinclude".as_ref(), b"subinclude".as_ref());
227 m.insert(b"subinclude".as_ref(), b"subinclude".as_ref());
242 m
228 m
243 };
229 };
244 }
230 }
245
231
246 pub type PatternTuple = (Vec<u8>, LineNumber, Vec<u8>);
232 pub type PatternTuple = (Vec<u8>, LineNumber, Vec<u8>);
247 type WarningTuple = (Vec<u8>, Vec<u8>);
233 type WarningTuple = (Vec<u8>, Vec<u8>);
248
234
249 pub fn parse_pattern_file_contents(
235 pub fn parse_pattern_file_contents(
250 lines: &[u8],
236 lines: &[u8],
251 file_path: &[u8],
237 file_path: &[u8],
252 warn: bool,
238 warn: bool,
253 ) -> (Vec<PatternTuple>, Vec<WarningTuple>) {
239 ) -> (Vec<PatternTuple>, Vec<WarningTuple>) {
254 let comment_regex = Regex::new(r"((?:^|[^\\])(?:\\\\)*)#.*").unwrap();
240 let comment_regex = Regex::new(r"((?:^|[^\\])(?:\\\\)*)#.*").unwrap();
255 let comment_escape_regex = Regex::new(r"\\#").unwrap();
241 let comment_escape_regex = Regex::new(r"\\#").unwrap();
256 let mut inputs: Vec<PatternTuple> = vec![];
242 let mut inputs: Vec<PatternTuple> = vec![];
257 let mut warnings: Vec<WarningTuple> = vec![];
243 let mut warnings: Vec<WarningTuple> = vec![];
258
244
259 let mut current_syntax = b"relre:".as_ref();
245 let mut current_syntax = b"relre:".as_ref();
260
246
261 for (line_number, mut line) in lines.split(|c| *c == b'\n').enumerate() {
247 for (line_number, mut line) in lines.split(|c| *c == b'\n').enumerate() {
262 let line_number = line_number + 1;
248 let line_number = line_number + 1;
263
249
264 let line_buf;
250 let line_buf;
265 if line.contains(&b'#') {
251 if line.contains(&b'#') {
266 if let Some(cap) = comment_regex.captures(line) {
252 if let Some(cap) = comment_regex.captures(line) {
267 line = &line[..cap.get(1).unwrap().end()]
253 line = &line[..cap.get(1).unwrap().end()]
268 }
254 }
269 line_buf = comment_escape_regex.replace_all(line, NoExpand(b"#"));
255 line_buf = comment_escape_regex.replace_all(line, NoExpand(b"#"));
270 line = &line_buf;
256 line = &line_buf;
271 }
257 }
272
258
273 let mut line = line.trim_end();
259 let mut line = line.trim_end();
274
260
275 if line.is_empty() {
261 if line.is_empty() {
276 continue;
262 continue;
277 }
263 }
278
264
279 if let Some(syntax) = line.drop_prefix(b"syntax:") {
265 if let Some(syntax) = line.drop_prefix(b"syntax:") {
280 let syntax = syntax.trim();
266 let syntax = syntax.trim();
281
267
282 if let Some(rel_syntax) = SYNTAXES.get(syntax) {
268 if let Some(rel_syntax) = SYNTAXES.get(syntax) {
283 current_syntax = rel_syntax;
269 current_syntax = rel_syntax;
284 } else if warn {
270 } else if warn {
285 warnings.push((file_path.to_owned(), syntax.to_owned()));
271 warnings.push((file_path.to_owned(), syntax.to_owned()));
286 }
272 }
287 continue;
273 continue;
288 }
274 }
289
275
290 let mut line_syntax: &[u8] = &current_syntax;
276 let mut line_syntax: &[u8] = &current_syntax;
291
277
292 for (s, rels) in SYNTAXES.iter() {
278 for (s, rels) in SYNTAXES.iter() {
293 if let Some(rest) = line.drop_prefix(rels) {
279 if let Some(rest) = line.drop_prefix(rels) {
294 line_syntax = rels;
280 line_syntax = rels;
295 line = rest;
281 line = rest;
296 break;
282 break;
297 }
283 }
298 if let Some(rest) = line.drop_prefix(&[s, &b":"[..]].concat()) {
284 if let Some(rest) = line.drop_prefix(&[s, &b":"[..]].concat()) {
299 line_syntax = rels;
285 line_syntax = rels;
300 line = rest;
286 line = rest;
301 break;
287 break;
302 }
288 }
303 }
289 }
304
290
305 inputs.push((
291 inputs.push((
306 [line_syntax, line].concat(),
292 [line_syntax, line].concat(),
307 line_number,
293 line_number,
308 line.to_owned(),
294 line.to_owned(),
309 ));
295 ));
310 }
296 }
311 (inputs, warnings)
297 (inputs, warnings)
312 }
298 }
313
299
314 pub fn read_pattern_file(
300 pub fn read_pattern_file(
315 file_path: &[u8],
301 file_path: &[u8],
316 warn: bool,
302 warn: bool,
317 ) -> Result<(Vec<PatternTuple>, Vec<WarningTuple>), PatternFileError> {
303 ) -> Result<(Vec<PatternTuple>, Vec<WarningTuple>), PatternFileError> {
318 let mut f = File::open(get_path_from_bytes(file_path))?;
304 let mut f = File::open(get_path_from_bytes(file_path))?;
319 let mut contents = Vec::new();
305 let mut contents = Vec::new();
320
306
321 f.read_to_end(&mut contents)?;
307 f.read_to_end(&mut contents)?;
322
308
323 Ok(parse_pattern_file_contents(&contents, file_path, warn))
309 Ok(parse_pattern_file_contents(&contents, file_path, warn))
324 }
310 }
325
311
326 #[cfg(test)]
312 #[cfg(test)]
327 mod tests {
313 mod tests {
328 use super::*;
314 use super::*;
329
315
330 #[test]
316 #[test]
331 fn escape_pattern_test() {
317 fn escape_pattern_test() {
332 let untouched = br#"!"%',/0123456789:;<=>@ABCDEFGHIJKLMNOPQRSTUVWXYZ_`abcdefghijklmnopqrstuvwxyz"#;
318 let untouched = br#"!"%',/0123456789:;<=>@ABCDEFGHIJKLMNOPQRSTUVWXYZ_`abcdefghijklmnopqrstuvwxyz"#;
333 assert_eq!(escape_pattern(untouched), untouched.to_vec());
319 assert_eq!(escape_pattern(untouched), untouched.to_vec());
334 // All escape codes
320 // All escape codes
335 assert_eq!(
321 assert_eq!(
336 escape_pattern(br#"()[]{}?*+-|^$\\.&~# \t\n\r\v\f"#),
322 escape_pattern(br#"()[]{}?*+-|^$\\.&~# \t\n\r\v\f"#),
337 br#"\(\)\[\]\{\}\?\*\+\-\|\^\$\\\\\.\&\~\#\ \\t\\n\\r\\v\\f"#
323 br#"\(\)\[\]\{\}\?\*\+\-\|\^\$\\\\\.\&\~\#\ \\t\\n\\r\\v\\f"#
338 .to_vec()
324 .to_vec()
339 );
325 );
340 }
326 }
341
327
342 #[test]
328 #[test]
343 fn glob_test() {
329 fn glob_test() {
344 assert_eq!(glob_to_re(br#"?"#), br#"."#);
330 assert_eq!(glob_to_re(br#"?"#), br#"."#);
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#"**/a"#), br#"(?:.*/)?a"#);
333 assert_eq!(glob_to_re(br#"**/a"#), br#"(?:.*/)?a"#);
348 assert_eq!(glob_to_re(br#"a/**/b"#), br#"a/(?:.*/)?b"#);
334 assert_eq!(glob_to_re(br#"a/**/b"#), br#"a/(?:.*/)?b"#);
349 assert_eq!(glob_to_re(br#"[a*?!^][^b][!c]"#), br#"[a*?!^][\^b][^c]"#);
335 assert_eq!(glob_to_re(br#"[a*?!^][^b][!c]"#), br#"[a*?!^][\^b][^c]"#);
350 assert_eq!(glob_to_re(br#"{a,b}"#), br#"(?:a|b)"#);
336 assert_eq!(glob_to_re(br#"{a,b}"#), br#"(?:a|b)"#);
351 assert_eq!(glob_to_re(br#".\*\?"#), br#"\.\*\?"#);
337 assert_eq!(glob_to_re(br#".\*\?"#), br#"\.\*\?"#);
352 }
338 }
353
339
354 #[test]
340 #[test]
355 fn test_parse_pattern_file_contents() {
341 fn test_parse_pattern_file_contents() {
356 let lines = b"syntax: glob\n*.elc";
342 let lines = b"syntax: glob\n*.elc";
357
343
358 assert_eq!(
344 assert_eq!(
359 vec![(b"relglob:*.elc".to_vec(), 2, b"*.elc".to_vec())],
345 vec![(b"relglob:*.elc".to_vec(), 2, b"*.elc".to_vec())],
360 parse_pattern_file_contents(lines, b"file_path", false).0,
346 parse_pattern_file_contents(lines, b"file_path", false).0,
361 );
347 );
362
348
363 let lines = b"syntax: include\nsyntax: glob";
349 let lines = b"syntax: include\nsyntax: glob";
364
350
365 assert_eq!(
351 assert_eq!(
366 parse_pattern_file_contents(lines, b"file_path", false).0,
352 parse_pattern_file_contents(lines, b"file_path", false).0,
367 vec![]
353 vec![]
368 );
354 );
369 let lines = b"glob:**.o";
355 let lines = b"glob:**.o";
370 assert_eq!(
356 assert_eq!(
371 parse_pattern_file_contents(lines, b"file_path", false).0,
357 parse_pattern_file_contents(lines, b"file_path", false).0,
372 vec![(b"relglob:**.o".to_vec(), 1, b"**.o".to_vec())]
358 vec![(b"relglob:**.o".to_vec(), 1, b"**.o".to_vec())]
373 );
359 );
374 }
360 }
375
361
376 #[test]
362 #[test]
377 fn test_build_single_regex_shortcut() {
363 fn test_build_single_regex_shortcut() {
378 assert_eq!(
364 assert_eq!(
379 br"(?:/|$)".to_vec(),
365 br"(?:/|$)".to_vec(),
380 build_single_regex(b"rootglob", b"", b"").unwrap()
366 build_single_regex(b"rootglob", b"", b"").unwrap()
381 );
367 );
382 assert_eq!(
368 assert_eq!(
383 br"whatever(?:/|$)".to_vec(),
369 br"whatever(?:/|$)".to_vec(),
384 build_single_regex(b"rootglob", b"whatever", b"").unwrap()
370 build_single_regex(b"rootglob", b"whatever", b"").unwrap()
385 );
371 );
386 assert_eq!(
372 assert_eq!(
387 br"[^/]*\.o".to_vec(),
373 br"[^/]*\.o".to_vec(),
388 build_single_regex(b"rootglob", b"*.o", b"").unwrap()
374 build_single_regex(b"rootglob", b"*.o", b"").unwrap()
389 );
375 );
390 }
376 }
391 }
377 }
General Comments 0
You need to be logged in to leave comments. Login now