diff options
| author | nsfisis <nsfisis@gmail.com> | 2026-06-14 14:06:06 +0900 |
|---|---|---|
| committer | nsfisis <nsfisis@gmail.com> | 2026-06-14 14:06:06 +0900 |
| commit | ba0a7f913b4b2a83d0c8862d657bdc867730a962 (patch) | |
| tree | 3ebc50ea1e3f1da11c3129a31ecf5f0356a6c239 /crates/shirabe/src/package/archiver | |
| parent | db7f5bd91c9d43d2d0b66824ed60571f1c478422 (diff) | |
| download | php-shirabe-ba0a7f913b4b2a83d0c8862d657bdc867730a962.tar.gz php-shirabe-ba0a7f913b4b2a83d0c8862d657bdc867730a962.tar.zst php-shirabe-ba0a7f913b4b2a83d0c8862d657bdc867730a962.zip | |
fix(archiver): resolve infinite recursion in exclude filter dispatch
The Phase B port invented an ArchivableFilesFilter trait whose filter
impls called self.filter, recursing forever; the name also collided
with the real ArchivableFilesFilter FilterIterator class. Composer has
no such interface: the finder holds GitExcludeFilter/ComposerExcludeFilter
(both extending BaseExcludeFilter) and calls $filter->filter().
Drop the invented trait, type the filters as Box<dyn BaseExcludeFilter>,
and implement BaseExcludeFilter on the concrete filters (accessors
delegating to inner). Remove the unused generic default methods from the
trait so it is dyn-compatible; they remain as inherent methods on
BaseExcludeFilterBase, which is where every caller already reaches them.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Diffstat (limited to 'crates/shirabe/src/package/archiver')
4 files changed, 32 insertions, 73 deletions
diff --git a/crates/shirabe/src/package/archiver/archivable_files_finder.rs b/crates/shirabe/src/package/archiver/archivable_files_finder.rs index c6a2d39..cb1c3e5 100644 --- a/crates/shirabe/src/package/archiver/archivable_files_finder.rs +++ b/crates/shirabe/src/package/archiver/archivable_files_finder.rs @@ -1,5 +1,6 @@ //! ref: composer/src/Composer/Package/Archiver/ArchivableFilesFinder.php +use crate::package::archiver::BaseExcludeFilter; use crate::package::archiver::ComposerExcludeFilter; use crate::package::archiver::GitExcludeFilter; use crate::util::Filesystem; @@ -35,7 +36,7 @@ impl ArchivableFilesFinder { } let sources = fs.normalize_path(&sources_real_path.unwrap()); - let filters: Vec<Box<dyn ArchivableFilesFilter>> = if ignore_filters { + let filters: Vec<Box<dyn BaseExcludeFilter>> = if ignore_filters { vec![] } else { vec![ @@ -97,22 +98,6 @@ impl ArchivableFilesFinder { } } -trait ArchivableFilesFilter { - fn filter(&self, relative_path: &str, exclude: bool) -> bool; -} - -impl ArchivableFilesFilter for GitExcludeFilter { - fn filter(&self, relative_path: &str, exclude: bool) -> bool { - self.filter(relative_path, exclude) - } -} - -impl ArchivableFilesFilter for ComposerExcludeFilter { - fn filter(&self, relative_path: &str, exclude: bool) -> bool { - self.filter(relative_path, exclude) - } -} - impl Iterator for ArchivableFilesFinder { type Item = PathBuf; diff --git a/crates/shirabe/src/package/archiver/base_exclude_filter.rs b/crates/shirabe/src/package/archiver/base_exclude_filter.rs index 1bd5b85..fa2007f 100644 --- a/crates/shirabe/src/package/archiver/base_exclude_filter.rs +++ b/crates/shirabe/src/package/archiver/base_exclude_filter.rs @@ -93,60 +93,4 @@ pub trait BaseExcludeFilter { exclude } - - /// Processes a file containing exclude rules of different formats per line - 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().to_string(); - if line.is_empty() || line.starts_with('#') { - return None; - } - line_parser(&line) - }) - .collect() - } - - /// Generates a set of exclude patterns for filter() from gitignore rules - fn generate_patterns(&self, rules: Vec<String>) -> Vec<(String, bool, bool)> { - rules - .into_iter() - .map(|rule| self.generate_pattern(&rule)) - .collect() - } - - /// Generates an exclude pattern for filter() from a gitignore rule - 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, true, true); - let rule_regex = &glob_regex[2..glob_regex.len() - 2]; - - ( - format!("{{{}{}(?=$|/)}}", pattern, rule_regex), - negate, - false, - ) - } } diff --git a/crates/shirabe/src/package/archiver/composer_exclude_filter.rs b/crates/shirabe/src/package/archiver/composer_exclude_filter.rs index e685a4a..953984d 100644 --- a/crates/shirabe/src/package/archiver/composer_exclude_filter.rs +++ b/crates/shirabe/src/package/archiver/composer_exclude_filter.rs @@ -1,5 +1,6 @@ //! ref: composer/src/Composer/Package/Archiver/ComposerExcludeFilter.php +use super::BaseExcludeFilter; use super::BaseExcludeFilterBase; #[derive(Debug)] @@ -14,3 +15,17 @@ impl ComposerExcludeFilter { Self { inner } } } + +impl BaseExcludeFilter for ComposerExcludeFilter { + fn source_path(&self) -> &str { + &self.inner.source_path + } + + fn exclude_patterns(&self) -> &[(String, bool, bool)] { + &self.inner.exclude_patterns + } + + fn exclude_patterns_mut(&mut self) -> &mut Vec<(String, bool, bool)> { + &mut self.inner.exclude_patterns + } +} diff --git a/crates/shirabe/src/package/archiver/git_exclude_filter.rs b/crates/shirabe/src/package/archiver/git_exclude_filter.rs index aeaed0a..e8765db 100644 --- a/crates/shirabe/src/package/archiver/git_exclude_filter.rs +++ b/crates/shirabe/src/package/archiver/git_exclude_filter.rs @@ -1,5 +1,6 @@ //! ref: composer/src/Composer/Package/Archiver/GitExcludeFilter.php +use crate::package::archiver::BaseExcludeFilter; use crate::package::archiver::BaseExcludeFilterBase; use shirabe_external_packages::composer::pcre::Preg; use std::path::Path; @@ -50,3 +51,17 @@ impl GitExcludeFilter { None } } + +impl BaseExcludeFilter for GitExcludeFilter { + fn source_path(&self) -> &str { + &self.inner.source_path + } + + fn exclude_patterns(&self) -> &[(String, bool, bool)] { + &self.inner.exclude_patterns + } + + fn exclude_patterns_mut(&mut self) -> &mut Vec<(String, bool, bool)> { + &mut self.inner.exclude_patterns + } +} |
