aboutsummaryrefslogtreecommitdiffhomepage
path: root/crates
diff options
context:
space:
mode:
authornsfisis <nsfisis@gmail.com>2026-05-15 00:55:38 +0900
committernsfisis <nsfisis@gmail.com>2026-05-15 19:51:17 +0900
commit2523443bf77002d1d1fcfaae8be140702d2a75d0 (patch)
tree75bff9509c36445f7b7d2de47c51407b4ea05d89 /crates
parentc6a7f1e0e861f7725dc546ec6f490722a6027fcf (diff)
downloadphp-shirabe-2523443bf77002d1d1fcfaae8be140702d2a75d0.tar.gz
php-shirabe-2523443bf77002d1d1fcfaae8be140702d2a75d0.tar.zst
php-shirabe-2523443bf77002d1d1fcfaae8be140702d2a75d0.zip
feat(port): port ArchivableFilesFinder.php
Diffstat (limited to 'crates')
-rw-r--r--crates/shirabe-php-shim/src/lib.rs4
-rw-r--r--crates/shirabe/src/package/archiver/archivable_files_finder.rs124
2 files changed, 128 insertions, 0 deletions
diff --git a/crates/shirabe-php-shim/src/lib.rs b/crates/shirabe-php-shim/src/lib.rs
index d04e3a6..393a485 100644
--- a/crates/shirabe-php-shim/src/lib.rs
+++ b/crates/shirabe-php-shim/src/lib.rs
@@ -356,6 +356,10 @@ pub fn json_encode(value: &PhpMixed) -> Option<String> {
todo!()
}
+pub fn preg_quote(str: &str, delimiter: Option<char>) -> String {
+ todo!()
+}
+
pub fn dirname(path: &str) -> String {
todo!()
}
diff --git a/crates/shirabe/src/package/archiver/archivable_files_finder.rs b/crates/shirabe/src/package/archiver/archivable_files_finder.rs
index e8cc3ae..7d1b94e 100644
--- a/crates/shirabe/src/package/archiver/archivable_files_finder.rs
+++ b/crates/shirabe/src/package/archiver/archivable_files_finder.rs
@@ -1 +1,125 @@
//! ref: composer/src/Composer/Package/Archiver/ArchivableFilesFinder.php
+
+use shirabe_external_packages::composer::pcre::preg::Preg;
+use shirabe_external_packages::symfony::component::finder::finder::Finder;
+use shirabe_external_packages::symfony::component::finder::spl_file_info::SplFileInfo;
+use shirabe_php_shim::{preg_quote, realpath, RuntimeException};
+use crate::package::archiver::composer_exclude_filter::ComposerExcludeFilter;
+use crate::package::archiver::git_exclude_filter::GitExcludeFilter;
+use crate::util::filesystem::Filesystem;
+
+pub struct ArchivableFilesFinder {
+ pub(crate) finder: Finder,
+ inner_iter: Box<dyn Iterator<Item = SplFileInfo>>,
+}
+
+impl std::fmt::Debug for ArchivableFilesFinder {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ f.debug_struct("ArchivableFilesFinder")
+ .field("finder", &self.finder)
+ .finish()
+ }
+}
+
+impl ArchivableFilesFinder {
+ pub fn new(sources: &str, excludes: Vec<String>, ignore_filters: bool) -> anyhow::Result<Self> {
+ let fs = Filesystem::new();
+
+ let sources_real_path = realpath(sources);
+ if sources_real_path.is_none() {
+ return Err(RuntimeException {
+ message: format!("Could not realpath() the source directory \"{}\"", sources),
+ code: 0,
+ }.into());
+ }
+ let sources = fs.normalize_path(&sources_real_path.unwrap());
+
+ let filters: Vec<Box<dyn ArchivableFilesFilter>> = if ignore_filters {
+ vec![]
+ } else {
+ vec![
+ Box::new(GitExcludeFilter::new(&sources)),
+ Box::new(ComposerExcludeFilter::new(&sources, excludes)),
+ ]
+ };
+
+ let mut finder = Finder::new();
+
+ let sources_clone = sources.clone();
+ let filter = move |file: &SplFileInfo| -> bool {
+ let realpath = file.get_real_path();
+ if realpath.is_none() {
+ return false;
+ }
+ let realpath = realpath.unwrap();
+ if file.is_link() && !realpath.starts_with(sources_clone.as_str()) {
+ return false;
+ }
+
+ let relative_path = Preg::replace(
+ &format!("^{}", preg_quote(&sources_clone, Some('#'))),
+ "",
+ &fs.normalize_path(&realpath),
+ );
+
+ let mut exclude = false;
+ for f in &filters {
+ exclude = f.filter(&relative_path, exclude);
+ }
+
+ !exclude
+ };
+
+ finder
+ .in_dir(&sources)
+ .filter(Box::new(filter))
+ .ignore_vcs(true)
+ .ignore_dot_files(false)
+ .sort_by_name();
+
+ let inner_iter = finder.get_iterator();
+
+ Ok(Self { finder, inner_iter })
+ }
+
+ pub fn accept(&self, current: &SplFileInfo) -> bool {
+ if !current.is_dir() {
+ return true;
+ }
+
+ let path = current.to_string();
+ match std::fs::read_dir(&path) {
+ Ok(mut iter) => iter.next().is_none(),
+ Err(_) => false,
+ }
+ }
+}
+
+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 = SplFileInfo;
+
+ fn next(&mut self) -> Option<Self::Item> {
+ loop {
+ let item = self.inner_iter.next()?;
+ if self.accept(&item) {
+ return Some(item);
+ }
+ }
+ }
+}