aboutsummaryrefslogtreecommitdiffhomepage
path: root/crates/shirabe
diff options
context:
space:
mode:
authornsfisis <nsfisis@gmail.com>2026-05-15 23:55:22 +0900
committernsfisis <nsfisis@gmail.com>2026-05-16 10:00:40 +0900
commit02e3c8170ee6fedc1d7c4129736e38881b768d7d (patch)
tree2f5c7aaa1688b0bdfe771ffabef044e3c71f43ba /crates/shirabe
parentb546fbdb2db838cd6fa5e3d4e1fc7ae10b1c7dae (diff)
downloadphp-shirabe-02e3c8170ee6fedc1d7c4129736e38881b768d7d.tar.gz
php-shirabe-02e3c8170ee6fedc1d7c4129736e38881b768d7d.tar.zst
php-shirabe-02e3c8170ee6fedc1d7c4129736e38881b768d7d.zip
feat(port): port RuleSet.php
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Diffstat (limited to 'crates/shirabe')
-rw-r--r--crates/shirabe/src/dependency_resolver/rule_set.rs153
1 files changed, 153 insertions, 0 deletions
diff --git a/crates/shirabe/src/dependency_resolver/rule_set.rs b/crates/shirabe/src/dependency_resolver/rule_set.rs
index f766c3c..36009d9 100644
--- a/crates/shirabe/src/dependency_resolver/rule_set.rs
+++ b/crates/shirabe/src/dependency_resolver/rule_set.rs
@@ -1 +1,154 @@
//! ref: composer/src/Composer/DependencyResolver/RuleSet.php
+
+use indexmap::IndexMap;
+use shirabe_php_shim::OutOfBoundsException;
+
+use crate::dependency_resolver::pool::Pool;
+use crate::dependency_resolver::request::Request;
+use crate::dependency_resolver::rule::Rule;
+use crate::dependency_resolver::rule_set_iterator::RuleSetIterator;
+use crate::repository::repository_set::RepositorySet;
+
+#[derive(Debug)]
+pub struct RuleSet {
+ pub rule_by_id: IndexMap<i64, Rule>,
+ pub(crate) rules: IndexMap<i64, Vec<Rule>>,
+ pub(crate) next_rule_id: i64,
+ pub(crate) rules_by_hash: IndexMap<String, Vec<Rule>>,
+}
+
+impl RuleSet {
+ pub const TYPE_PACKAGE: i64 = 0;
+ pub const TYPE_REQUEST: i64 = 1;
+ pub const TYPE_LEARNED: i64 = 4;
+
+ pub fn types() -> IndexMap<i64, &'static str> {
+ let mut map = IndexMap::new();
+ map.insert(Self::TYPE_PACKAGE, "PACKAGE");
+ map.insert(Self::TYPE_REQUEST, "REQUEST");
+ map.insert(Self::TYPE_LEARNED, "LEARNED");
+ map
+ }
+
+ pub fn new() -> Self {
+ let mut rules = IndexMap::new();
+ for type_ in Self::get_types_static() {
+ rules.insert(type_, vec![]);
+ }
+ Self {
+ rule_by_id: IndexMap::new(),
+ rules,
+ next_rule_id: 0,
+ rules_by_hash: IndexMap::new(),
+ }
+ }
+
+ fn get_types_static() -> Vec<i64> {
+ Self::types().into_keys().collect()
+ }
+
+ pub fn add(&mut self, rule: Rule, r#type: i64) -> anyhow::Result<()> {
+ let types = Self::types();
+ if !types.contains_key(&r#type) {
+ return Err(OutOfBoundsException {
+ message: format!("Unknown rule type: {}", r#type),
+ code: 0,
+ }.into());
+ }
+
+ let hash = rule.get_hash();
+
+ if let Some(potential_duplicates) = self.rules_by_hash.get(&hash) {
+ for potential_duplicate in potential_duplicates {
+ if rule.equals(potential_duplicate) {
+ return Ok(());
+ }
+ }
+ }
+
+ self.rules.entry(r#type).or_insert_with(Vec::new).push(rule.clone());
+ rule.set_type(r#type);
+ self.rule_by_id.insert(self.next_rule_id, rule.clone());
+
+ self.next_rule_id += 1;
+
+ self.rules_by_hash.entry(hash).or_insert_with(Vec::new).push(rule);
+
+ Ok(())
+ }
+
+ pub fn count(&self) -> i64 {
+ self.next_rule_id
+ }
+
+ pub fn rule_by_id(&self, id: i64) -> &Rule {
+ &self.rule_by_id[&id]
+ }
+
+ pub fn get_rules(&self) -> &IndexMap<i64, Vec<Rule>> {
+ &self.rules
+ }
+
+ pub fn get_iterator(&self) -> RuleSetIterator {
+ RuleSetIterator::new(self.get_rules().clone())
+ }
+
+ pub fn get_iterator_for(&self, types: Vec<i64>) -> RuleSetIterator {
+ let all_rules = self.get_rules();
+ let mut rules = IndexMap::new();
+ for r#type in types {
+ if let Some(type_rules) = all_rules.get(&r#type) {
+ rules.insert(r#type, type_rules.clone());
+ }
+ }
+ RuleSetIterator::new(rules)
+ }
+
+ pub fn get_iterator_without(&self, types: Vec<i64>) -> RuleSetIterator {
+ let mut rules = self.get_rules().clone();
+ for r#type in types {
+ rules.remove(&r#type);
+ }
+ RuleSetIterator::new(rules)
+ }
+
+ pub fn get_types(&self) -> Vec<i64> {
+ Self::get_types_static()
+ }
+
+ pub fn get_pretty_string(
+ &self,
+ repository_set: Option<&RepositorySet>,
+ request: Option<&Request>,
+ pool: Option<&Pool>,
+ is_verbose: bool,
+ ) -> String {
+ let types = Self::types();
+ let mut string = "\n".to_string();
+ for (r#type, rules) in &self.rules {
+ let type_name = types.get(r#type).copied().unwrap_or("");
+ string.push_str(&format!("{:<8}: ", type_name));
+ for rule in rules {
+ if repository_set.is_some() && request.is_some() && pool.is_some() {
+ string.push_str(&rule.get_pretty_string(
+ repository_set.unwrap(),
+ request.unwrap(),
+ pool.unwrap(),
+ is_verbose,
+ ));
+ } else {
+ string.push_str(&rule.to_string());
+ }
+ string.push('\n');
+ }
+ string.push_str("\n\n");
+ }
+ string
+ }
+}
+
+impl std::fmt::Display for RuleSet {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ write!(f, "{}", self.get_pretty_string(None, None, None, false))
+ }
+}