diff options
| author | nsfisis <nsfisis@gmail.com> | 2026-05-15 23:30:51 +0900 |
|---|---|---|
| committer | nsfisis <nsfisis@gmail.com> | 2026-05-16 10:00:40 +0900 |
| commit | 260c69c974b96d6854d55d1f61740bfdaaf25fe3 (patch) | |
| tree | 91c6fe5ec9a62eec437bb270a9b0020f690d1e8f /crates/shirabe/src/package/archiver | |
| parent | 401878dafc00b720a7e7de7a5d9fd9615c0e2d69 (diff) | |
| download | php-shirabe-260c69c974b96d6854d55d1f61740bfdaaf25fe3.tar.gz php-shirabe-260c69c974b96d6854d55d1f61740bfdaaf25fe3.tar.zst php-shirabe-260c69c974b96d6854d55d1f61740bfdaaf25fe3.zip | |
feat(port): port BaseExcludeFilter.php
Diffstat (limited to 'crates/shirabe/src/package/archiver')
| -rw-r--r-- | crates/shirabe/src/package/archiver/base_exclude_filter.rs | 83 |
1 files changed, 83 insertions, 0 deletions
diff --git a/crates/shirabe/src/package/archiver/base_exclude_filter.rs b/crates/shirabe/src/package/archiver/base_exclude_filter.rs index 6bdb9a4..7f737fe 100644 --- a/crates/shirabe/src/package/archiver/base_exclude_filter.rs +++ b/crates/shirabe/src/package/archiver/base_exclude_filter.rs @@ -1 +1,84 @@ //! ref: composer/src/Composer/Package/Archiver/BaseExcludeFilter.php + +use shirabe_external_packages::composer::pcre::preg::Preg; +use shirabe_external_packages::symfony::component::finder::glob::Glob; + +#[derive(Debug)] +pub struct BaseExcludeFilter { + pub(crate) source_path: String, + pub(crate) exclude_patterns: Vec<(String, bool, bool)>, +} + +impl BaseExcludeFilter { + pub fn new(source_path: String) -> Self { + Self { + source_path, + exclude_patterns: vec![], + } + } + + pub fn filter(&self, relative_path: &str, mut exclude: bool) -> bool { + for (pattern, negate, strip_leading_slash) in &self.exclude_patterns { + let path = if *strip_leading_slash { + &relative_path[1..] + } else { + relative_path + }; + + // suppressed RuntimeException, equivalent to PHP try-catch + if let Ok(matched) = Preg::is_match(pattern, path) { + if matched { + exclude = !negate; + } + } + } + + exclude + } + + pub fn parse_lines<F>(&self, lines: Vec<String>, line_parser: F) -> Vec<(String, bool, bool)> + where + F: Fn(&str) -> Option<(String, bool, bool)>, + { + lines + .into_iter() + .filter_map(|line| { + let line = line.trim(); + if line.is_empty() || line.starts_with('#') { + return None; + } + line_parser(line) + }) + .collect() + } + + pub fn generate_patterns(&self, rules: Vec<String>) -> Vec<(String, bool, bool)> { + rules.into_iter().map(|rule| self.generate_pattern(&rule)).collect() + } + + pub fn generate_pattern(&self, rule: &str) -> (String, bool, bool) { + let mut negate = false; + let mut pattern = String::new(); + + let mut rule = rule.to_string(); + if !rule.is_empty() && rule.starts_with('!') { + negate = true; + rule = rule.trim_start_matches('!').to_string(); + } + + let first_slash_position = rule.find('/'); + if first_slash_position == Some(0) { + pattern = "^/".to_string(); + } else if first_slash_position.is_none() || first_slash_position == Some(rule.len() - 1) { + pattern = "/".to_string(); + } + + let rule = rule.trim_matches('/'); + + // remove delimiters as well as caret (^) and dollar sign ($) from the regex + let glob_regex = Glob::to_regex(rule); + let rule_regex = &glob_regex[2..glob_regex.len() - 2]; + + (format!("{}{}(?=$|/)", pattern, rule_regex), negate, false) + } +} |
