cargo_zng/res/built_in/
glob.rs1use super::*;
2
3const GLOB_HELP: &str = "
4Copy all matches in place
5
6The request file:
7 source/l10n/fluent-files.zr-glob
8 | # localization dir
9 | l10n
10 | # only Fluent files
11 | **/*.ftl
12 | # except test locales
13 | !:**/pseudo*
14
15Copies all '.ftl' not in a *pseudo* path to:
16 target/l10n/
17
18The first path pattern is required and defines the entries that
19will be copied, an initial pattern with '**' flattens the matches.
20The path is relative to the Cargo workspace root.
21
22The subsequent patterns are optional and filter each file or dir selected by
23the first pattern. The paths are relative to each match, if it is a file
24the filters apply to the file name only, if it is a dir the filters apply to
25the dir and descendants.
26
27The glob pattern syntax is:
28
29 ? — matches any single character.
30 * — matches any (possibly empty) sequence of characters.
31 ** — matches the current directory and arbitrary subdirectories.
32 [c] — matches any character inside the brackets.
33[a-z] — matches any characters in the Unicode sequence.
34 [!b] — negates the brackets match.
35
36And in filter patterns only:
37
38!:pattern — negates the entire pattern.
39
40";
41pub(super) fn glob() {
42 help(GLOB_HELP);
43
44 let target = path(ZR_TARGET);
46 let target = target.parent().unwrap();
47
48 let request_path = path(ZR_REQUEST);
49 let mut lines = read_lines(&request_path);
50 let (ln, selection) = lines
51 .next()
52 .unwrap_or_else(|| fatal!("expected at least one path pattern"))
53 .unwrap_or_else(|e| fatal!("{e}"));
54
55 let selection = ::glob::glob(&selection).unwrap_or_else(|e| fatal!("at line {ln}, {e}"));
57 let mut filters = vec![];
59 for r in lines {
60 let (ln, filter) = r.unwrap_or_else(|e| fatal!("{e}"));
61 let (filter, matches_if) = if let Some(f) = filter.strip_prefix("!:") {
62 (f, false)
63 } else {
64 (filter.as_str(), true)
65 };
66 let pat = ::glob::Pattern::new(filter).unwrap_or_else(|e| fatal!("at line {ln}, {e}"));
67 filters.push((pat, matches_if));
68 }
69 let selection = {
71 let mut s = vec![];
72 for entry in selection {
73 s.push(entry.unwrap_or_else(|e| fatal!("{e}")));
74 }
75 s.sort();
77 s
78 };
79
80 let mut any = false;
81
82 'apply: for source in selection {
83 if source.is_dir() {
84 let filters_root = source.parent().map(Path::to_owned).unwrap_or_default();
85 'copy_dir: for entry in walkdir::WalkDir::new(&source).sort_by_file_name() {
86 let source = entry.unwrap_or_else(|e| fatal!("cannot walkdir entry `{}`, {e}", source.display()));
87 let source = source.path();
88 let match_source = source.strip_prefix(&filters_root).unwrap();
90 for (filter, matches_if) in &filters {
91 if filter.matches_path(match_source) != *matches_if {
92 continue 'copy_dir;
93 }
94 }
95 let target = target.join(match_source);
96
97 any = true;
98 if source.is_dir() {
99 fs::create_dir_all(&target).unwrap_or_else(|e| fatal!("cannot create dir `{}`, {e}", source.display()));
100 } else {
101 if let Some(p) = &target.parent() {
102 fs::create_dir_all(p).unwrap_or_else(|e| fatal!("cannot create dir `{}`, {e}", p.display()));
103 }
104 fs::copy(source, &target)
105 .unwrap_or_else(|e| fatal!("cannot copy `{}` to `{}`, {e}", source.display(), target.display()));
106 }
107 println!("{}", display_path(&target));
108 }
109 } else if source.is_file() {
110 let source_name = source.file_name().unwrap().to_string_lossy();
112 for (filter, matches_if) in &filters {
113 if filter.matches(&source_name) != *matches_if {
114 continue 'apply;
115 }
116 }
117 let target = target.join(source_name.as_ref());
118
119 any = true;
120 fs::copy(&source, &target).unwrap_or_else(|e| fatal!("cannot copy `{}` to `{}`, {e}", source.display(), target.display()));
121 println!("{}", display_path(&target));
122 } else if source.is_symlink() {
123 symlink_warn(&source);
124 }
125 }
126
127 if !any {
128 warn!("no match")
129 }
130}