includer_codegen/utils.rs
1//! Tools to build custom `Pipeline`s that aren't already provided.
2//!
3//! Using the tools here to build custom [`Pipelines`] can help keep your
4//! pipelines more consistent, and easier to use for others.
5//!
6//! [`Pipelines`]: ../trait.Pipeline.html
7use self::Filter::*;
8use regex::Regex;
9use std::path::Path;
10
11/// The type of filter list to use.
12pub enum FilterListType {
13 Blacklist,
14 Whitelist,
15}
16
17/// A filter to determine if a file or path should be added to the `Pipeline`.
18pub enum Filter {
19 Include(FilterRule),
20 Exclude(FilterRule),
21}
22
23impl Filter {
24 /// Create a filter that includes a file extension.
25 ///
26 /// Can panic, see [`FilterRule::extension`].
27 ///
28 /// [`FilterRule::extension`]: ./enum.FilterRule.html#method.extension
29 pub fn include_extension<S: Into<String>>(ext: S) -> Self {
30 Include(FilterRule::extension(ext))
31 }
32
33 /// Create a filter that excludes a file extension.
34 ///
35 /// Can panic, see [`FilterRule::extension`].
36 ///
37 /// [`FilterRule::extension`]: ./enum.FilterRule.html#method.extension
38 pub fn exclude_extension<S: Into<String>>(ext: S) -> Self {
39 Exclude(FilterRule::extension(ext))
40 }
41
42 /// Create a filter that includes a regex.
43 ///
44 /// Can panic, see [`FilterRule::regex`].
45 ///
46 /// [`FilterRule::regex`]: ./enum.FilterRule.html#method.regex
47 pub fn include_regex<S: AsRef<str>>(regex_str: S) -> Self {
48 Include(FilterRule::regex(regex_str))
49 }
50
51 /// Create a filter that excludes a regex.
52 ///
53 /// Can panic, see [`FilterRule::regex`].
54 ///
55 /// [`FilterRule::regex`]: ./enum.FilterRule.html#method.regex
56 pub fn exclude_regex<S: AsRef<str>>(regex_str: S) -> Self {
57 Exclude(FilterRule::regex(regex_str))
58 }
59
60 pub fn matches<P: AsRef<Path>>(&self, relative_path: P) -> bool {
61 match self {
62 Include(rule) => rule.matches(relative_path),
63 Exclude(rule) => rule.matches(relative_path),
64 }
65 }
66}
67
68/// A rule on how to match a file or path
69pub enum FilterRule {
70 /// Match any file that contains the specified extension.
71 ///
72 /// It is suggested to use the [`extension`] helper method instead for
73 /// ease of use and consistency.
74 ///
75 /// ```
76 /// # use includer_codegen::utils::FilterRule;
77 /// #
78 /// let html_files = FilterRule::Extension("html".to_string());
79 /// let inconsistent = FilterRule::Extension(".html".to_string());
80 /// ```
81 ///
82 /// Note: For consistency, the extension should *not* have a leading period
83 ///
84 /// [`extension`]: #method.extension
85 Extension(String),
86
87 /// Match any file that has a regex match on its path.
88 ///
89 /// It is suggested to use the [`regex`] helper method instead for
90 /// ease of use and consistency.
91 ///
92 /// ```
93 /// # use includer_codegen::utils::FilterRule;
94 /// use includer_codegen::regex::Regex;
95 ///
96 /// // Match all css files in the "styles" subdirectory
97 /// let css_files = FilterRule::Regex(Regex::new(r"^styles[/\\].*\.css$").unwrap());
98 /// ```
99 ///
100 /// Note: For consistency, the path compared to the regex should be
101 /// relative to the root asset path with no leading slash. You can get
102 /// this result by using [`strip_prefix`].
103 ///
104 /// [`regex`]: #method.regex
105 /// [`strip_prefix`]: https://doc.rust-lang.org/std/path/struct.Path.html#method.strip_prefix
106 Regex(Regex),
107}
108
109impl FilterRule {
110 /// Creates a validated `FilterRule::Extension`
111 ///
112 /// Makes sure that the extension does not have a leading `"."`.
113 ///
114 /// ```
115 /// # use includer_codegen::utils::FilterRule;
116 /// #
117 /// // Only accept HTML files
118 /// FilterRule::extension("html");
119 /// ```
120 ///
121 /// # Panics
122 ///
123 /// This function will panic if extension is not valid.
124 ///
125 /// ```should_panic
126 /// # use includer_codegen::utils::FilterRule;
127 /// #
128 /// // should panic
129 /// FilterRule::extension(".html");
130 /// ```
131 pub fn extension<S: Into<String>>(extension: S) -> Self {
132 let ext = extension.into();
133
134 if &ext[0..1] == "." {
135 panic!("Filter::Extension should not contain a period prefix!");
136 }
137
138 FilterRule::Extension(ext)
139 }
140
141 /// Creates a validated `Filer::Regex`
142 ///
143 /// ```
144 /// # use includer_codegen::utils::FilterRule;
145 /// #
146 /// // Accept all css files that are under the root subdirectory `styles` (multi-platform)
147 /// FilterRule::regex(r"^styles[/\\].*\.css$");
148 /// ```
149 ///
150 /// # Panics
151 ///
152 /// Invalid regex expressions will panic.
153 ///
154 /// ```should_panic
155 /// # use includer_codegen::utils::FilterRule;
156 /// #
157 /// // should panic
158 /// FilterRule::regex(r"\h");
159 /// ```
160 pub fn regex<S: AsRef<str>>(regex_str: S) -> Self {
161 let regex = Regex::new(regex_str.as_ref());
162 FilterRule::Regex(regex.unwrap())
163 }
164
165 /// See if the path matches the filter rule
166 pub fn matches<P: AsRef<Path>>(&self, relative_path: P) -> bool {
167 let path = relative_path.as_ref();
168 match self {
169 FilterRule::Extension(ext) => path.extension() == Some(ext.as_ref()),
170 FilterRule::Regex(re) => re.is_match(
171 path.to_str()
172 .expect("Path couldn't be represented by a str"),
173 ),
174 }
175 }
176}
177
178pub(crate) fn path_to_string<P: AsRef<Path>>(path: P) -> String {
179 path.as_ref()
180 .to_str()
181 .expect("Unable to represent path as str")
182 .to_string()
183}
184
185/// Makes Cargo re-run build script if path has changed since last build.
186///
187/// Note this only has an effect inside a build script, as it just prints a
188/// Cargo interpreted key to stdout. See [`reference`].
189///
190/// # Panics
191///
192/// Panics if the path cannot be casted to a str.
193///
194/// [`reference`]: https://doc.rust-lang.org/cargo/reference/build-scripts.html#outputs-of-the-build-script
195pub fn watch_path<P: AsRef<Path>>(p: P) {
196 println!("cargo:rerun-if-changed={}", p.as_ref().to_str().unwrap());
197}