aboutsummaryrefslogtreecommitdiffhomepage
path: root/crates
diff options
context:
space:
mode:
authornsfisis <nsfisis@gmail.com>2026-05-15 23:15:05 +0900
committernsfisis <nsfisis@gmail.com>2026-05-16 10:00:39 +0900
commit7011204a8267137967e55b3e6016b88f2e4a600d (patch)
tree13bb802e1088ac47dbf9ac6e3cea220d8d53168c /crates
parent4ac20be1cb04443d193d9e99a0d1037563ee0d13 (diff)
downloadphp-shirabe-7011204a8267137967e55b3e6016b88f2e4a600d.tar.gz
php-shirabe-7011204a8267137967e55b3e6016b88f2e4a600d.tar.zst
php-shirabe-7011204a8267137967e55b3e6016b88f2e4a600d.zip
feat(port): port PackageSorter.php
Diffstat (limited to 'crates')
-rw-r--r--crates/shirabe-php-shim/src/lib.rs4
-rw-r--r--crates/shirabe/src/util/package_sorter.rs110
2 files changed, 114 insertions, 0 deletions
diff --git a/crates/shirabe-php-shim/src/lib.rs b/crates/shirabe-php-shim/src/lib.rs
index 4a8d035..072d458 100644
--- a/crates/shirabe-php-shim/src/lib.rs
+++ b/crates/shirabe-php-shim/src/lib.rs
@@ -521,3 +521,7 @@ pub fn html_entity_decode(s: &str) -> String {
pub fn hash_file(algo: &str, filename: &str) -> Option<String> {
todo!()
}
+
+pub fn strnatcasecmp(s1: &str, s2: &str) -> i64 {
+ todo!()
+}
diff --git a/crates/shirabe/src/util/package_sorter.rs b/crates/shirabe/src/util/package_sorter.rs
index 45d299f..5a7aa84 100644
--- a/crates/shirabe/src/util/package_sorter.rs
+++ b/crates/shirabe/src/util/package_sorter.rs
@@ -1 +1,111 @@
//! ref: composer/src/Composer/Util/PackageSorter.php
+
+use std::any::Any;
+
+use indexmap::IndexMap;
+use shirabe_php_shim::{strnatcasecmp, version_compare};
+
+use crate::package::link::Link;
+use crate::package::package_interface::PackageInterface;
+use crate::package::root_package::RootPackage;
+
+pub struct PackageSorter;
+
+impl PackageSorter {
+ pub fn get_most_current_version(packages: Vec<Box<dyn PackageInterface>>) -> Option<Box<dyn PackageInterface>> {
+ if packages.is_empty() {
+ return None;
+ }
+
+ let mut iter = packages.into_iter();
+ let mut highest = iter.next().unwrap();
+ for candidate in iter {
+ if candidate.is_default_branch() {
+ return Some(candidate);
+ }
+ if version_compare(highest.get_version(), candidate.get_version(), "<") {
+ highest = candidate;
+ }
+ }
+
+ Some(highest)
+ }
+
+ pub fn sort_packages_alphabetically(mut packages: Vec<Box<dyn PackageInterface>>) -> Vec<Box<dyn PackageInterface>> {
+ packages.sort_by_key(|p| p.get_name());
+ packages
+ }
+
+ pub fn sort_packages(packages: Vec<Box<dyn PackageInterface>>, weights: IndexMap<String, i64>) -> Vec<Box<dyn PackageInterface>> {
+ let mut usage_list: IndexMap<String, Vec<String>> = IndexMap::new();
+
+ for package in &packages {
+ let mut links: IndexMap<String, Link> = package.get_requires();
+ // TODO: check for RootAliasPackage as well
+ if let Some(root_package) = (package.as_any() as &dyn Any).downcast_ref::<RootPackage>() {
+ links.extend(root_package.get_dev_requires());
+ }
+ for link in links.values() {
+ let target = link.get_target().to_string();
+ usage_list.entry(target).or_default().push(package.get_name().to_string());
+ }
+ }
+
+ let mut helper = ComputeImportanceHelper {
+ computing: IndexMap::new(),
+ computed: IndexMap::new(),
+ usage_list: &usage_list,
+ weights: &weights,
+ };
+
+ let mut weighted_packages: Vec<(String, i64, usize)> = Vec::new();
+ for (index, package) in packages.iter().enumerate() {
+ let name = package.get_name().to_string();
+ let weight = helper.compute(&name);
+ weighted_packages.push((name, weight, index));
+ }
+
+ weighted_packages.sort_by(|a, b| {
+ if a.1 != b.1 {
+ a.1.cmp(&b.1)
+ } else {
+ strnatcasecmp(&a.0, &b.0).cmp(&0)
+ }
+ });
+
+ let mut packages: Vec<Option<Box<dyn PackageInterface>>> = packages.into_iter().map(Some).collect();
+ weighted_packages
+ .into_iter()
+ .map(|(_, _, index)| packages[index].take().unwrap())
+ .collect()
+ }
+}
+
+struct ComputeImportanceHelper<'a> {
+ computing: IndexMap<String, bool>,
+ computed: IndexMap<String, i64>,
+ usage_list: &'a IndexMap<String, Vec<String>>,
+ weights: &'a IndexMap<String, i64>,
+}
+
+impl ComputeImportanceHelper<'_> {
+ fn compute(&mut self, name: &str) -> i64 {
+ if let Some(&w) = self.computed.get(name) {
+ return w;
+ }
+ if self.computing.contains_key(name) {
+ return 0;
+ }
+ self.computing.insert(name.to_string(), true);
+ let mut weight = *self.weights.get(name).unwrap_or(&0);
+ if let Some(users) = self.usage_list.get(name) {
+ let users = users.clone();
+ for user in &users {
+ weight -= 1 - self.compute(user);
+ }
+ }
+ self.computing.remove(name);
+ self.computed.insert(name.to_string(), weight);
+ weight
+ }
+}