|
1 |
| -use crate::glob::optimize_public_source_entry; |
| 1 | +use crate::glob::split_pattern; |
2 | 2 | use crate::GlobEntry;
|
3 | 3 | use bexpand::Expression;
|
4 | 4 | use std::path::PathBuf;
|
| 5 | +use tracing::{event, Level}; |
5 | 6 |
|
6 | 7 | use super::auto_source_detection::IGNORED_CONTENT_DIRS;
|
7 | 8 |
|
@@ -75,25 +76,103 @@ impl Sources {
|
75 | 76 | }
|
76 | 77 |
|
77 | 78 | impl PublicSourceEntry {
|
78 |
| - #[cfg(test)] |
79 |
| - pub fn from_pattern(dir: PathBuf, pattern: &str) -> Self { |
80 |
| - let mut parts = pattern.split_whitespace(); |
81 |
| - let _ = parts.next().unwrap_or_default(); |
82 |
| - let not_or_pattern = parts.next().unwrap_or_default(); |
83 |
| - if not_or_pattern == "not" { |
84 |
| - let pattern = parts.next().unwrap_or_default(); |
85 |
| - return Self { |
86 |
| - base: dir.to_string_lossy().into(), |
87 |
| - pattern: pattern[1..pattern.len() - 1].to_string(), |
88 |
| - negated: true, |
| 79 | + /// Optimize the PublicSourceEntry by trying to move all the static parts of the pattern to the |
| 80 | + /// base of the PublicSourceEntry. |
| 81 | + /// |
| 82 | + /// ```diff |
| 83 | + /// - { base: '/', pattern: 'src/**/*.html'} |
| 84 | + /// + { base: '/src', pattern: '**/*.html'} |
| 85 | + /// ``` |
| 86 | + /// |
| 87 | + /// A file stays in the `pattern` part, because the `base` should only be a directory. |
| 88 | + /// |
| 89 | + /// ```diff |
| 90 | + /// - { base: '/', pattern: 'src/examples/index.html'} |
| 91 | + /// + { base: '/src/examples', pattern: 'index.html'} |
| 92 | + /// ``` |
| 93 | + /// |
| 94 | + /// A folder will be moved to the `base` part, and the `pattern` will be set to `**/*`. |
| 95 | + /// |
| 96 | + /// ```diff |
| 97 | + /// - { base: '/', pattern: 'src/examples'} |
| 98 | + /// + { base: '/src/examples', pattern: '**/*'} |
| 99 | + /// ``` |
| 100 | + /// |
| 101 | + /// In addition, we will canonicalize the base path so we always work with the correctly |
| 102 | + /// resolved path. |
| 103 | + pub fn optimize(&mut self) { |
| 104 | + // Resolve base path immediately |
| 105 | + let Ok(base) = dunce::canonicalize(&self.base) else { |
| 106 | + event!(Level::ERROR, "Failed to resolve base: {:?}", self.base); |
| 107 | + return; |
| 108 | + }; |
| 109 | + self.base = base.to_string_lossy().to_string(); |
| 110 | + |
| 111 | + // No dynamic part, figure out if we are dealing with a file or a directory. |
| 112 | + if !self.pattern.contains('*') { |
| 113 | + let combined_path = if self.pattern.starts_with("/") { |
| 114 | + PathBuf::from(&self.pattern) |
| 115 | + } else { |
| 116 | + PathBuf::from(&self.base).join(&self.pattern) |
89 | 117 | };
|
90 |
| - } |
91 | 118 |
|
92 |
| - Self { |
93 |
| - base: dir.to_string_lossy().into(), |
94 |
| - pattern: not_or_pattern[1..not_or_pattern.len() - 1].to_string(), |
95 |
| - negated: false, |
| 119 | + match dunce::canonicalize(combined_path) { |
| 120 | + Ok(resolved_path) if resolved_path.is_dir() => { |
| 121 | + self.base = resolved_path.to_string_lossy().to_string(); |
| 122 | + self.pattern = "**/*".to_owned(); |
| 123 | + } |
| 124 | + Ok(resolved_path) if resolved_path.is_file() => { |
| 125 | + self.base = resolved_path |
| 126 | + .parent() |
| 127 | + .unwrap() |
| 128 | + .to_string_lossy() |
| 129 | + .to_string(); |
| 130 | + // Ensure leading slash, otherwise it will match against all files in all folders/ |
| 131 | + self.pattern = format!( |
| 132 | + "/{}", |
| 133 | + resolved_path |
| 134 | + .file_name() |
| 135 | + .unwrap() |
| 136 | + .to_string_lossy() |
| 137 | + .to_string() |
| 138 | + ); |
| 139 | + } |
| 140 | + _ => {} |
| 141 | + } |
| 142 | + return; |
96 | 143 | }
|
| 144 | + |
| 145 | + // Contains dynamic part |
| 146 | + let (static_part, dynamic_part) = split_pattern(&self.pattern); |
| 147 | + |
| 148 | + let base: PathBuf = self.base.clone().into(); |
| 149 | + let base = match static_part { |
| 150 | + Some(static_part) => base.join(static_part), |
| 151 | + None => base, |
| 152 | + }; |
| 153 | + |
| 154 | + // TODO: If the base does not exist on disk, try removing the last slash and try again. |
| 155 | + let base = match dunce::canonicalize(&base) { |
| 156 | + Ok(base) => base, |
| 157 | + Err(err) => { |
| 158 | + event!(tracing::Level::ERROR, "Failed to resolve glob: {:?}", err); |
| 159 | + return; |
| 160 | + } |
| 161 | + }; |
| 162 | + |
| 163 | + let pattern = match dynamic_part { |
| 164 | + Some(dynamic_part) => dynamic_part, |
| 165 | + None => { |
| 166 | + if base.is_dir() { |
| 167 | + "**/*".to_owned() |
| 168 | + } else { |
| 169 | + "".to_owned() |
| 170 | + } |
| 171 | + } |
| 172 | + }; |
| 173 | + |
| 174 | + self.base = base.to_string_lossy().to_string(); |
| 175 | + self.pattern = pattern; |
97 | 176 | }
|
98 | 177 | }
|
99 | 178 |
|
@@ -139,7 +218,7 @@ pub fn public_source_entries_to_private_source_entries(
|
139 | 218 | .collect::<Vec<_>>()
|
140 | 219 | })
|
141 | 220 | .map(|mut public_source| {
|
142 |
| - optimize_public_source_entry(&mut public_source); |
| 221 | + public_source.optimize(); |
143 | 222 | public_source
|
144 | 223 | })
|
145 | 224 | .collect::<Vec<_>>();
|
|
0 commit comments