diff options
| author | nsfisis <nsfisis@gmail.com> | 2026-05-23 23:14:52 +0900 |
|---|---|---|
| committer | nsfisis <nsfisis@gmail.com> | 2026-05-23 23:15:14 +0900 |
| commit | dbdecaf5a1c54a876b7ee0153d58dd39b1080f97 (patch) | |
| tree | f13f2ced03c803dcbc42a5672458b3cb19ff0f30 | |
| parent | f5b987a00712211b7ce56300851182bda904e97b (diff) | |
| download | php-shirabe-dbdecaf5a1c54a876b7ee0153d58dd39b1080f97.tar.gz php-shirabe-dbdecaf5a1c54a876b7ee0153d58dd39b1080f97.tar.zst php-shirabe-dbdecaf5a1c54a876b7ee0153d58dd39b1080f97.zip | |
refactor(semver): change ConstraintInterface to a closed enum
Replace the dyn ConstraintInterface trait objects with an AnyConstraint
enum closing over its four implementors (Simple, Multi, MatchAll,
MatchNone), mirroring the earlier Rule enum conversion. Rename
constraint.rs to simple_constraint.rs to match the renamed Constraint
type.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
65 files changed, 1134 insertions, 1079 deletions
diff --git a/crates/shirabe-external-packages/src/composer/semver/compiling_matcher.rs b/crates/shirabe-external-packages/src/composer/semver/compiling_matcher.rs index 246c4e5..b075ba7 100644 --- a/crates/shirabe-external-packages/src/composer/semver/compiling_matcher.rs +++ b/crates/shirabe-external-packages/src/composer/semver/compiling_matcher.rs @@ -1,18 +1,18 @@ -use shirabe_semver::constraint::ConstraintInterface; +use shirabe_semver::constraint::AnyConstraint; #[derive(Debug)] pub struct CompilingMatcher; impl CompilingMatcher { - pub fn r#match(_constraint: &dyn ConstraintInterface, _package: &dyn std::any::Any) -> bool { + pub fn r#match(_constraint: &AnyConstraint, _package: &dyn std::any::Any) -> bool { todo!() } - pub fn matches(_constraint: &dyn ConstraintInterface, _operator: i64, _version: &str) -> bool { + pub fn matches(_constraint: &AnyConstraint, _operator: i64, _version: &str) -> bool { todo!() } - pub fn match_(_constraint: &dyn ConstraintInterface, _operator: i64, _version: &str) -> bool { + pub fn match_(_constraint: &AnyConstraint, _operator: i64, _version: &str) -> bool { todo!() } diff --git a/crates/shirabe-external-packages/src/composer/semver/intervals.rs b/crates/shirabe-external-packages/src/composer/semver/intervals.rs index ff04cd8..bf8a74f 100644 --- a/crates/shirabe-external-packages/src/composer/semver/intervals.rs +++ b/crates/shirabe-external-packages/src/composer/semver/intervals.rs @@ -1,28 +1,26 @@ use shirabe_php_shim::PhpMixed; -use shirabe_semver::constraint::ConstraintInterface; +use shirabe_semver::constraint::AnyConstraint; #[derive(Debug)] pub struct Intervals; impl Intervals { pub fn is_subset_of( - _constraint_a: &dyn ConstraintInterface, - _constraint_b: &dyn ConstraintInterface, + _constraint_a: &AnyConstraint, + _constraint_b: &AnyConstraint, ) -> anyhow::Result<bool> { todo!() } - pub fn compact_constraint( - _constraint: Box<dyn ConstraintInterface>, - ) -> Box<dyn ConstraintInterface> { + pub fn compact_constraint(_constraint: AnyConstraint) -> AnyConstraint { todo!() } - pub fn compact(_constraint: &dyn ConstraintInterface) -> Box<dyn ConstraintInterface> { + pub fn compact(_constraint: &AnyConstraint) -> AnyConstraint { todo!() } - pub fn get(_constraint: &dyn ConstraintInterface) -> anyhow::Result<PhpMixed> { + pub fn get(_constraint: &AnyConstraint) -> anyhow::Result<PhpMixed> { todo!() } diff --git a/crates/shirabe-semver/src/comparator.rs b/crates/shirabe-semver/src/comparator.rs index 6db06d4..36a4142 100644 --- a/crates/shirabe-semver/src/comparator.rs +++ b/crates/shirabe-semver/src/comparator.rs @@ -1,6 +1,6 @@ //! ref: composer/vendor/composer/semver/src/Comparator.php -use crate::constraint::Constraint; +use crate::constraint::SimpleConstraint; pub struct Comparator; @@ -30,7 +30,10 @@ impl Comparator { } pub fn compare(version1: String, operator: String, version2: String) -> bool { - let constraint = Constraint::new(operator, version2); - constraint.match_specific(&Constraint::new("==".to_string(), version1), true) + let constraint = SimpleConstraint::new(operator, version2, None); + constraint.match_specific( + &SimpleConstraint::new("==".to_string(), version1, None), + true, + ) } } diff --git a/crates/shirabe-semver/src/compiling_matcher.rs b/crates/shirabe-semver/src/compiling_matcher.rs index 737647f..d7e203e 100644 --- a/crates/shirabe-semver/src/compiling_matcher.rs +++ b/crates/shirabe-semver/src/compiling_matcher.rs @@ -5,8 +5,8 @@ use std::sync::OnceLock; use indexmap::IndexMap; -use crate::constraint::Constraint; -use crate::constraint::ConstraintInterface; +use crate::constraint::AnyConstraint; +use crate::constraint::SimpleConstraint; static COMPILED_CHECKER_CACHE: OnceLock< Mutex<IndexMap<String, Box<dyn Fn(String, bool) -> bool + Send + Sync>>>, @@ -16,12 +16,12 @@ static RESULT_CACHE: OnceLock<Mutex<IndexMap<String, bool>>> = OnceLock::new(); // Rust does not support eval(), so the compiled checker path is always disabled. // The COMPILED_CHECKER_CACHE is retained structurally but never populated. static TRANS_OP_INT: &[(i64, &str)] = &[ - (Constraint::OP_EQ, Constraint::STR_OP_EQ), - (Constraint::OP_LT, Constraint::STR_OP_LT), - (Constraint::OP_LE, Constraint::STR_OP_LE), - (Constraint::OP_GT, Constraint::STR_OP_GT), - (Constraint::OP_GE, Constraint::STR_OP_GE), - (Constraint::OP_NE, Constraint::STR_OP_NE), + (SimpleConstraint::OP_EQ, SimpleConstraint::STR_OP_EQ), + (SimpleConstraint::OP_LT, SimpleConstraint::STR_OP_LT), + (SimpleConstraint::OP_LE, SimpleConstraint::STR_OP_LE), + (SimpleConstraint::OP_GT, SimpleConstraint::STR_OP_GT), + (SimpleConstraint::OP_GE, SimpleConstraint::STR_OP_GE), + (SimpleConstraint::OP_NE, SimpleConstraint::STR_OP_NE), ]; pub struct CompilingMatcher; @@ -41,8 +41,8 @@ impl CompilingMatcher { Self::compiled_checker_cache().lock().unwrap().clear(); } - pub fn r#match(constraint: &dyn ConstraintInterface, operator: i64, version: String) -> bool { - let result_cache_key = format!("{}{};{}", operator, constraint.__to_string(), version); + pub fn r#match(constraint: &AnyConstraint, operator: i64, version: String) -> bool { + let result_cache_key = format!("{}{};{}", operator, constraint.to_string(), version); { let cache = Self::result_cache().lock().unwrap(); @@ -56,7 +56,8 @@ impl CompilingMatcher { .find(|(op, _)| *op == operator) .map(|(_, s)| *s) .expect("unknown operator"); - let result = constraint.matches(&Constraint::new(trans_op.to_string(), version)); + let result = + constraint.matches(&SimpleConstraint::new(trans_op.to_string(), version, None).into()); Self::result_cache() .lock() diff --git a/crates/shirabe-semver/src/constraint.rs b/crates/shirabe-semver/src/constraint.rs index 6a57a57..75c15b6 100644 --- a/crates/shirabe-semver/src/constraint.rs +++ b/crates/shirabe-semver/src/constraint.rs @@ -1,13 +1,13 @@ +mod any_constraint; mod bound; -mod constraint; -mod constraint_interface; mod match_all_constraint; mod match_none_constraint; mod multi_constraint; +mod simple_constraint; +pub use any_constraint::*; pub use bound::*; -pub use constraint::*; -pub use constraint_interface::*; pub use match_all_constraint::*; pub use match_none_constraint::*; pub use multi_constraint::*; +pub use simple_constraint::*; diff --git a/crates/shirabe-semver/src/constraint/any_constraint.rs b/crates/shirabe-semver/src/constraint/any_constraint.rs new file mode 100644 index 0000000..b61a36f --- /dev/null +++ b/crates/shirabe-semver/src/constraint/any_constraint.rs @@ -0,0 +1,169 @@ +//! ref: composer/vendor/composer/semver/src/Constraint/ConstraintInterface.php + +use crate::constraint::Bound; +use crate::constraint::MatchAllConstraint; +use crate::constraint::MatchNoneConstraint; +use crate::constraint::MultiConstraint; +use crate::constraint::SimpleConstraint; + +/// Corresponds to PHP's `ConstraintInterface`. +#[derive(Clone, Debug)] +pub enum AnyConstraint { + Simple(SimpleConstraint), + Multi(MultiConstraint), + MatchAll(MatchAllConstraint), + MatchNone(MatchNoneConstraint), +} + +impl AnyConstraint { + pub fn matches(&self, provider: &AnyConstraint) -> bool { + match self { + Self::MatchAll(_) => true, + Self::MatchNone(_) => false, + Self::Simple(c) => match provider.as_constraint() { + Some(p) => c.match_specific(p, false), + None => provider.matches(self), + }, + Self::Multi(m) => { + if !m.conjunctive { + m.constraints.iter().any(|sub| provider.matches(sub)) + } else if provider.is_disjunctive() { + provider.matches(self) + } else { + m.constraints.iter().all(|sub| provider.matches(sub)) + } + } + } + } + + pub fn compile(&self, other_operator: i64) -> String { + match self { + Self::Simple(c) => c.compile(other_operator), + Self::Multi(c) => c.compile(other_operator), + Self::MatchAll(c) => c.compile(other_operator), + Self::MatchNone(c) => c.compile(other_operator), + } + } + + pub fn get_upper_bound(&self) -> Bound { + match self { + Self::Simple(c) => c.get_upper_bound(), + Self::Multi(c) => c.get_upper_bound(), + Self::MatchAll(c) => c.get_upper_bound(), + Self::MatchNone(c) => c.get_upper_bound(), + } + } + + pub fn get_lower_bound(&self) -> Bound { + match self { + Self::Simple(c) => c.get_lower_bound(), + Self::Multi(c) => c.get_lower_bound(), + Self::MatchAll(c) => c.get_lower_bound(), + Self::MatchNone(c) => c.get_lower_bound(), + } + } + + pub fn get_pretty_string(&self) -> String { + match self { + Self::Simple(c) => c.get_pretty_string(), + Self::Multi(c) => c.get_pretty_string(), + Self::MatchAll(c) => c.get_pretty_string(), + Self::MatchNone(c) => c.get_pretty_string(), + } + } + + /// PHP `$c instanceof MultiConstraint && $c->isDisjunctive()`. + pub fn is_disjunctive(&self) -> bool { + matches!(self, Self::Multi(m) if !m.conjunctive) + } + + /// PHP `$c instanceof Constraint`. + pub fn is_constraint(&self) -> bool { + matches!(self, Self::Simple(_)) + } + + pub fn get_operator(&self) -> &'static str { + match self { + Self::Simple(c) => c.get_operator(), + _ => "", + } + } + + pub fn get_version(&self) -> &str { + match self { + Self::Simple(c) => c.get_version(), + _ => "", + } + } + + pub fn as_constraint(&self) -> Option<&SimpleConstraint> { + match self { + Self::Simple(c) => Some(c), + _ => None, + } + } + + pub fn as_multi_constraint(&self) -> Option<&MultiConstraint> { + match self { + Self::Multi(c) => Some(c), + _ => None, + } + } + + pub fn is_match_all(&self) -> bool { + matches!(self, Self::MatchAll(_)) + } + + pub fn is_match_none(&self) -> bool { + matches!(self, Self::MatchNone(_)) + } + + /// PHP exposes `ConstraintInterface::setPrettyString()` and defaults the + /// pretty string to the constraint's string form when unset. This port takes + /// the pretty string at construction instead; this setter exists only so + /// `MultiConstraint::create()` can apply the pretty string PHP sets on its + /// (possibly polymorphic) result. + pub(crate) fn set_pretty_string(&mut self, pretty_string: Option<String>) { + match self { + Self::Simple(c) => c.pretty_string = pretty_string, + Self::Multi(c) => c.pretty_string = pretty_string, + Self::MatchAll(c) => c.pretty_string = pretty_string, + Self::MatchNone(c) => c.pretty_string = pretty_string, + } + } +} + +impl From<SimpleConstraint> for AnyConstraint { + fn from(c: SimpleConstraint) -> Self { + Self::Simple(c) + } +} + +impl From<MultiConstraint> for AnyConstraint { + fn from(c: MultiConstraint) -> Self { + Self::Multi(c) + } +} + +impl From<MatchAllConstraint> for AnyConstraint { + fn from(c: MatchAllConstraint) -> Self { + Self::MatchAll(c) + } +} + +impl From<MatchNoneConstraint> for AnyConstraint { + fn from(c: MatchNoneConstraint) -> Self { + Self::MatchNone(c) + } +} + +impl std::fmt::Display for AnyConstraint { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::Simple(c) => write!(f, "{}", c), + Self::Multi(c) => write!(f, "{}", c), + Self::MatchAll(c) => write!(f, "{}", c), + Self::MatchNone(c) => write!(f, "{}", c), + } + } +} diff --git a/crates/shirabe-semver/src/constraint/constraint_interface.rs b/crates/shirabe-semver/src/constraint/constraint_interface.rs deleted file mode 100644 index f04ed2a..0000000 --- a/crates/shirabe-semver/src/constraint/constraint_interface.rs +++ /dev/null @@ -1,43 +0,0 @@ -//! ref: composer/vendor/composer/semver/src/Constraint/ConstraintInterface.php - -use crate::constraint::Bound; - -pub trait ConstraintInterface: std::fmt::Debug { - fn matches(&self, provider: &dyn ConstraintInterface) -> bool; - - fn compile(&self, other_operator: i64) -> String; - - fn get_upper_bound(&self) -> Bound; - - fn get_lower_bound(&self) -> Bound; - - fn get_pretty_string(&self) -> String; - - fn set_pretty_string(&mut self, pretty_string: Option<String>); - - fn __to_string(&self) -> String; - - // Rust-specific helpers for instanceof checks in MultiConstraint::matches and optimizeConstraints. - fn is_disjunctive(&self) -> bool { - false - } - - /// Rust-specific helper: PHP `$c instanceof Constraint` check. - fn is_constraint(&self) -> bool { - false - } - - /// Rust-specific helper: PHP `$c->getOperator()`. Only meaningful when `is_constraint()` is true. - fn get_operator(&self) -> &'static str { - "" - } - - /// Rust-specific helper: PHP `$c->getVersion()`. Only meaningful when `is_constraint()` is true. - fn get_version(&self) -> &str { - "" - } - - fn clone_box(&self) -> Box<dyn ConstraintInterface>; - - fn as_any(&self) -> &dyn std::any::Any; -} diff --git a/crates/shirabe-semver/src/constraint/match_all_constraint.rs b/crates/shirabe-semver/src/constraint/match_all_constraint.rs index b695ce2..8d4e870 100644 --- a/crates/shirabe-semver/src/constraint/match_all_constraint.rs +++ b/crates/shirabe-semver/src/constraint/match_all_constraint.rs @@ -1,68 +1,41 @@ //! ref: composer/vendor/composer/semver/src/Constraint/MatchAllConstraint.php use crate::constraint::Bound; -use crate::constraint::ConstraintInterface; -#[derive(Debug)] +#[derive(Debug, Clone, Default)] pub struct MatchAllConstraint { pub(crate) pretty_string: Option<String>, } impl MatchAllConstraint { - pub fn new() -> Self { - Self { - pretty_string: None, - } + pub fn new(pretty_string: Option<String>) -> Self { + Self { pretty_string } } -} - -impl Default for MatchAllConstraint { - fn default() -> Self { - Self::new() - } -} -impl ConstraintInterface for MatchAllConstraint { - fn matches(&self, _provider: &dyn ConstraintInterface) -> bool { - true - } - - fn compile(&self, _other_operator: i64) -> String { + pub fn compile(&self, _other_operator: i64) -> String { "true".to_string() } - fn set_pretty_string(&mut self, pretty_string: Option<String>) { - self.pretty_string = pretty_string; - } - - fn get_pretty_string(&self) -> String { + pub fn get_pretty_string(&self) -> String { if let Some(ref s) = self.pretty_string && !s.is_empty() { return s.clone(); } - self.__to_string() + self.to_string() } - fn __to_string(&self) -> String { - "*".to_string() - } - - fn clone_box(&self) -> Box<dyn ConstraintInterface> { - Box::new(MatchAllConstraint { - pretty_string: self.pretty_string.clone(), - }) - } - - fn as_any(&self) -> &dyn std::any::Any { - self - } - - fn get_upper_bound(&self) -> Bound { + pub fn get_upper_bound(&self) -> Bound { Bound::positive_infinity() } - fn get_lower_bound(&self) -> Bound { + pub fn get_lower_bound(&self) -> Bound { Bound::zero() } } + +impl std::fmt::Display for MatchAllConstraint { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "*") + } +} diff --git a/crates/shirabe-semver/src/constraint/match_none_constraint.rs b/crates/shirabe-semver/src/constraint/match_none_constraint.rs index 587058a..08cb319 100644 --- a/crates/shirabe-semver/src/constraint/match_none_constraint.rs +++ b/crates/shirabe-semver/src/constraint/match_none_constraint.rs @@ -1,54 +1,41 @@ //! ref: composer/vendor/composer/semver/src/Constraint/MatchNoneConstraint.php use crate::constraint::Bound; -use crate::constraint::ConstraintInterface; -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct MatchNoneConstraint { pub(crate) pretty_string: Option<String>, } -impl ConstraintInterface for MatchNoneConstraint { - fn matches(&self, _provider: &dyn ConstraintInterface) -> bool { - false +impl MatchNoneConstraint { + pub fn new(pretty_string: Option<String>) -> Self { + Self { pretty_string } } - fn compile(&self, _other_operator: i64) -> String { + pub fn compile(&self, _other_operator: i64) -> String { "false".to_string() } - fn set_pretty_string(&mut self, pretty_string: Option<String>) { - self.pretty_string = pretty_string; - } - - fn get_pretty_string(&self) -> String { + pub fn get_pretty_string(&self) -> String { if let Some(ref s) = self.pretty_string && !s.is_empty() { return s.clone(); } - self.__to_string() + self.to_string() } - fn __to_string(&self) -> String { - "[]".to_string() - } - - fn clone_box(&self) -> Box<dyn ConstraintInterface> { - Box::new(MatchNoneConstraint { - pretty_string: self.pretty_string.clone(), - }) - } - - fn as_any(&self) -> &dyn std::any::Any { - self + pub fn get_upper_bound(&self) -> Bound { + Bound::new("0.0.0.0-dev".to_string(), false) } - fn get_upper_bound(&self) -> Bound { + pub fn get_lower_bound(&self) -> Bound { Bound::new("0.0.0.0-dev".to_string(), false) } +} - fn get_lower_bound(&self) -> Bound { - Bound::new("0.0.0.0-dev".to_string(), false) +impl std::fmt::Display for MatchNoneConstraint { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "[]") } } diff --git a/crates/shirabe-semver/src/constraint/multi_constraint.rs b/crates/shirabe-semver/src/constraint/multi_constraint.rs index 4222095..05cc16e 100644 --- a/crates/shirabe-semver/src/constraint/multi_constraint.rs +++ b/crates/shirabe-semver/src/constraint/multi_constraint.rs @@ -1,30 +1,22 @@ //! ref: composer/vendor/composer/semver/src/Constraint/MultiConstraint.php -use std::cell::RefCell; - +use crate::constraint::AnyConstraint; use crate::constraint::Bound; -use crate::constraint::ConstraintInterface; use crate::constraint::MatchAllConstraint; +#[derive(Debug, Clone)] pub struct MultiConstraint { - pub(crate) constraints: Vec<Box<dyn ConstraintInterface>>, + pub(crate) constraints: Vec<AnyConstraint>, pub(crate) pretty_string: Option<String>, - string: RefCell<Option<String>>, pub(crate) conjunctive: bool, - lower_bound: RefCell<Option<Bound>>, - upper_bound: RefCell<Option<Bound>>, -} - -impl std::fmt::Debug for MultiConstraint { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.debug_struct("MultiConstraint") - .field("conjunctive", &self.conjunctive) - .finish() - } } impl MultiConstraint { - pub fn new(constraints: Vec<Box<dyn ConstraintInterface>>, conjunctive: bool) -> Self { + pub fn new( + constraints: Vec<AnyConstraint>, + conjunctive: bool, + pretty_string: Option<String>, + ) -> Self { assert!( constraints.len() >= 2, "Must provide at least two constraints for a MultiConstraint. Use \ @@ -34,15 +26,12 @@ impl MultiConstraint { Self { constraints, - pretty_string: None, - string: RefCell::new(None), + pretty_string, conjunctive, - lower_bound: RefCell::new(None), - upper_bound: RefCell::new(None), } } - pub fn get_constraints(&self) -> &[Box<dyn ConstraintInterface>] { + pub fn get_constraints(&self) -> &[AnyConstraint] { &self.constraints } @@ -54,11 +43,9 @@ impl MultiConstraint { !self.conjunctive } - fn extract_bounds(&self) { - if self.lower_bound.borrow().is_some() { - return; - } - + /// Composer memoizes the result; this port recomputes on every call. It is not heavy + /// calculation so caching is a premature optimization. + fn extract_bounds(&self) -> (Bound, Bound) { let mut current_lower: Option<Bound> = None; let mut current_upper: Option<Bound> = None; @@ -93,52 +80,61 @@ impl MultiConstraint { } } - *self.lower_bound.borrow_mut() = current_lower; - *self.upper_bound.borrow_mut() = current_upper; + ( + current_lower.expect("MultiConstraint always has at least two constraints"), + current_upper.expect("MultiConstraint always has at least two constraints"), + ) } pub fn create( - constraints: Vec<Box<dyn ConstraintInterface>>, + constraints: Vec<AnyConstraint>, conjunctive: bool, - ) -> anyhow::Result<Box<dyn ConstraintInterface>> { + pretty_string: Option<String>, + ) -> anyhow::Result<AnyConstraint> { if constraints.is_empty() { - return Ok(Box::new(MatchAllConstraint { - pretty_string: None, - })); + return Ok(MatchAllConstraint::new(pretty_string).into()); } if constraints.len() == 1 { - return Ok(constraints.into_iter().next().unwrap()); + let mut single = constraints.into_iter().next().unwrap(); + if pretty_string.is_some() { + single.set_pretty_string(pretty_string); + } + return Ok(single); } let (constraints, conjunctive) = Self::optimize_constraints(constraints, conjunctive); if constraints.len() == 1 { - return Ok(constraints.into_iter().next().unwrap()); + let mut single = constraints.into_iter().next().unwrap(); + if pretty_string.is_some() { + single.set_pretty_string(pretty_string); + } + return Ok(single); } - Ok(Box::new(MultiConstraint::new(constraints, conjunctive))) + Ok(MultiConstraint::new(constraints, conjunctive, pretty_string).into()) } // Returns the (possibly optimized) constraints and the effective conjunctive flag. // Always returns the constraints vector (consuming it), whether or not optimization was applied. // The PHP version returns null for no optimization; here we return the original values unchanged. fn optimize_constraints( - constraints: Vec<Box<dyn ConstraintInterface>>, + constraints: Vec<AnyConstraint>, conjunctive: bool, - ) -> (Vec<Box<dyn ConstraintInterface>>, bool) { + ) -> (Vec<AnyConstraint>, bool) { // Parse the two OR groups and if they are contiguous collapse into one constraint. // [>= 1 < 2] || [>= 2 < 3] || [>= 3 < 4] => [>= 1 < 4] if !conjunctive { let mut iter = constraints.into_iter(); - let mut left: Box<dyn ConstraintInterface> = iter.next().unwrap(); - let mut merged_constraints: Vec<Box<dyn ConstraintInterface>> = Vec::new(); + let mut left: AnyConstraint = iter.next().unwrap(); + let mut merged_constraints: Vec<AnyConstraint> = Vec::new(); let mut optimized = false; for right in iter { - let merged: Option<Box<dyn ConstraintInterface>> = { - let maybe_l_mc = left.as_any().downcast_ref::<MultiConstraint>(); - let maybe_r_mc = right.as_any().downcast_ref::<MultiConstraint>(); + let merged: Option<AnyConstraint> = { + let maybe_l_mc = left.as_multi_constraint(); + let maybe_r_mc = right.as_multi_constraint(); if let (Some(l_mc), Some(r_mc)) = (maybe_l_mc, maybe_r_mc) { if l_mc.conjunctive @@ -146,10 +142,10 @@ impl MultiConstraint { && l_mc.constraints.len() == 2 && r_mc.constraints.len() == 2 { - let left0 = l_mc.constraints[0].__to_string(); - let left1 = l_mc.constraints[1].__to_string(); - let right0 = r_mc.constraints[0].__to_string(); - let right1 = r_mc.constraints[1].__to_string(); + let left0 = l_mc.constraints[0].to_string(); + let left1 = l_mc.constraints[1].to_string(); + let right0 = r_mc.constraints[0].to_string(); + let right1 = r_mc.constraints[1].to_string(); if left0.starts_with(">=") && left1.starts_with('<') @@ -157,14 +153,17 @@ impl MultiConstraint { && right1.starts_with('<') && left1.get(2..) == right0.get(3..) { - Some(Box::new(MultiConstraint::new( - vec![ - l_mc.constraints[0].clone_box(), - r_mc.constraints[1].clone_box(), - ], - true, - )) - as Box<dyn ConstraintInterface>) + Some( + MultiConstraint::new( + vec![ + l_mc.constraints[0].clone(), + r_mc.constraints[1].clone(), + ], + true, + None, + ) + .into(), + ) } else { None } @@ -198,10 +197,8 @@ impl MultiConstraint { (constraints, conjunctive) } -} -impl ConstraintInterface for MultiConstraint { - fn compile(&self, other_operator: i64) -> String { + pub fn compile(&self, other_operator: i64) -> String { let mut parts = Vec::new(); for constraint in &self.constraints { let code = constraint.compile(other_operator); @@ -233,87 +230,28 @@ impl ConstraintInterface for MultiConstraint { } } - fn matches(&self, provider: &dyn ConstraintInterface) -> bool { - if !self.conjunctive { - for constraint in &self.constraints { - if provider.matches(constraint.as_ref()) { - return true; - } - } - return false; - } - - if provider.is_disjunctive() { - return provider.matches(self); - } - - for constraint in &self.constraints { - if !provider.matches(constraint.as_ref()) { - return false; - } - } - - true - } - - fn set_pretty_string(&mut self, pretty_string: Option<String>) { - self.pretty_string = pretty_string; - } - - fn get_pretty_string(&self) -> String { + pub fn get_pretty_string(&self) -> String { if let Some(ref s) = self.pretty_string && !s.is_empty() { return s.clone(); } - self.__to_string() - } - - fn __to_string(&self) -> String { - if let Some(ref s) = *self.string.borrow() { - return s.clone(); - } - - let parts: Vec<String> = self.constraints.iter().map(|c| c.__to_string()).collect(); - let sep = if self.conjunctive { " " } else { " || " }; - let result = format!("[{}]", parts.join(sep)); - - *self.string.borrow_mut() = Some(result.clone()); - result - } - - fn get_lower_bound(&self) -> Bound { - self.extract_bounds(); - self.lower_bound - .borrow() - .clone() - .expect("extractBounds should have populated the lowerBound property") - } - - fn get_upper_bound(&self) -> Bound { - self.extract_bounds(); - self.upper_bound - .borrow() - .clone() - .expect("extractBounds should have populated the upperBound property") + self.to_string() } - fn is_disjunctive(&self) -> bool { - !self.conjunctive + pub fn get_lower_bound(&self) -> Bound { + self.extract_bounds().0 } - fn clone_box(&self) -> Box<dyn ConstraintInterface> { - Box::new(MultiConstraint { - constraints: self.constraints.iter().map(|c| c.clone_box()).collect(), - pretty_string: self.pretty_string.clone(), - string: RefCell::new(self.string.borrow().clone()), - conjunctive: self.conjunctive, - lower_bound: RefCell::new(self.lower_bound.borrow().clone()), - upper_bound: RefCell::new(self.upper_bound.borrow().clone()), - }) + pub fn get_upper_bound(&self) -> Bound { + self.extract_bounds().1 } +} - fn as_any(&self) -> &dyn std::any::Any { - self +impl std::fmt::Display for MultiConstraint { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let parts: Vec<String> = self.constraints.iter().map(|c| c.to_string()).collect(); + let sep = if self.conjunctive { " " } else { " || " }; + write!(f, "[{}]", parts.join(sep)) } } diff --git a/crates/shirabe-semver/src/constraint/constraint.rs b/crates/shirabe-semver/src/constraint/simple_constraint.rs index 3ac633d..161425a 100644 --- a/crates/shirabe-semver/src/constraint/constraint.rs +++ b/crates/shirabe-semver/src/constraint/simple_constraint.rs @@ -1,23 +1,19 @@ //! ref: composer/vendor/composer/semver/src/Constraint/Constraint.php -use std::sync::Mutex; - use anyhow::bail; use shirabe_php_shim as php; use crate::constraint::Bound; -use crate::constraint::ConstraintInterface; -#[derive(Debug)] -pub struct Constraint { +/// Corresponds to PHP's `Constraint`. +#[derive(Debug, Clone)] +pub struct SimpleConstraint { pub(crate) operator: i64, pub(crate) version: String, pub(crate) pretty_string: Option<String>, - pub(crate) lower_bound: Mutex<Option<Bound>>, - pub(crate) upper_bound: Mutex<Option<Bound>>, } -impl Constraint { +impl SimpleConstraint { pub const OP_EQ: i64 = 0; pub const OP_LT: i64 = 1; pub const OP_LE: i64 = 2; @@ -60,8 +56,7 @@ impl Constraint { } } - pub fn new(operator: impl Into<String>, version: impl Into<String>) -> Self { - let operator: String = operator.into(); + pub fn new(operator: String, version: String, pretty_string: Option<String>) -> Self { let op_int = Self::trans_op_str(&operator).unwrap_or_else(|| { // PHP raises InvalidArgumentException; in the Rust port keep that as a panic // because invalid operators are programmer errors caught during porting. @@ -74,10 +69,8 @@ impl Constraint { Self { operator: op_int, - version: version.into(), - pretty_string: None, - lower_bound: Mutex::new(None), - upper_bound: Mutex::new(None), + version, + pretty_string, } } @@ -225,7 +218,7 @@ impl Constraint { format!("!$b && {}", code_comparison) } - pub fn match_specific(&self, provider: &Constraint, compare_branches: bool) -> bool { + pub fn match_specific(&self, provider: &SimpleConstraint, compare_branches: bool) -> bool { let no_equal_op = Self::trans_op_int(self.operator).replace('=', ""); let provider_no_equal_op = Self::trans_op_int(provider.operator).replace('=', ""); @@ -286,18 +279,14 @@ impl Constraint { false } - fn extract_bounds(&self) { - if self.lower_bound.lock().unwrap().is_some() { - return; - } - + /// Composer memoizes the result; this port recomputes on every call. It is not heavy + /// calculation so caching is a premature optimization. + fn extract_bounds(&self) -> (Bound, Bound) { if self.version.starts_with("dev-") { - *self.lower_bound.lock().unwrap() = Some(Bound::zero()); - *self.upper_bound.lock().unwrap() = Some(Bound::positive_infinity()); - return; + return (Bound::zero(), Bound::positive_infinity()); } - let (lower, upper) = match self.operator { + match self.operator { Self::OP_EQ => ( Bound::new(self.version.clone(), true), Bound::new(self.version.clone(), true), @@ -314,96 +303,33 @@ impl Constraint { ), Self::OP_NE => (Bound::zero(), Bound::positive_infinity()), _ => panic!("unknown operator: {}", self.operator), - }; - - *self.lower_bound.lock().unwrap() = Some(lower); - *self.upper_bound.lock().unwrap() = Some(upper); - } -} - -impl Clone for Constraint { - fn clone(&self) -> Self { - Self { - operator: self.operator, - version: self.version.clone(), - pretty_string: self.pretty_string.clone(), - lower_bound: Mutex::new(self.lower_bound.lock().unwrap().clone()), - upper_bound: Mutex::new(self.upper_bound.lock().unwrap().clone()), } } -} -impl ConstraintInterface for Constraint { - fn matches(&self, provider: &dyn ConstraintInterface) -> bool { - if let Some(p) = provider.as_any().downcast_ref::<Constraint>() { - return self.match_specific(p, false); - } - provider.matches(self) - } - - fn compile(&self, other_operator: i64) -> String { + pub fn compile(&self, other_operator: i64) -> String { self.compile_constraint(other_operator) } - fn set_pretty_string(&mut self, pretty_string: Option<String>) { - self.pretty_string = pretty_string; - } - - fn get_pretty_string(&self) -> String { + pub fn get_pretty_string(&self) -> String { if let Some(ref s) = self.pretty_string && !s.is_empty() { return s.clone(); } - self.__to_string() + self.to_string() } - fn __to_string(&self) -> String { - format!("{} {}", Self::trans_op_int(self.operator), self.version) + pub fn get_lower_bound(&self) -> Bound { + self.extract_bounds().0 } - fn get_lower_bound(&self) -> Bound { - self.extract_bounds(); - self.lower_bound - .lock() - .unwrap() - .clone() - .expect("extract_bounds should have set lower_bound") - } - - fn get_upper_bound(&self) -> Bound { - self.extract_bounds(); - self.upper_bound - .lock() - .unwrap() - .clone() - .expect("extract_bounds should have set upper_bound") - } - - fn clone_box(&self) -> Box<dyn ConstraintInterface> { - Box::new(self.clone()) - } - - fn as_any(&self) -> &dyn std::any::Any { - self - } - - fn is_constraint(&self) -> bool { - true - } - - fn get_operator(&self) -> &'static str { - Self::trans_op_int(self.operator) - } - - fn get_version(&self) -> &str { - &self.version + pub fn get_upper_bound(&self) -> Bound { + self.extract_bounds().1 } } -impl std::fmt::Display for Constraint { +impl std::fmt::Display for SimpleConstraint { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - use crate::constraint::ConstraintInterface; - write!(f, "{}", ConstraintInterface::__to_string(self)) + write!(f, "{} {}", Self::trans_op_int(self.operator), self.version) } } diff --git a/crates/shirabe-semver/src/interval.rs b/crates/shirabe-semver/src/interval.rs index b1f3010..7f27514 100644 --- a/crates/shirabe-semver/src/interval.rs +++ b/crates/shirabe-semver/src/interval.rs @@ -1,8 +1,6 @@ //! ref: composer/vendor/composer/semver/src/Interval.php -use std::sync::OnceLock; - -use crate::constraint::Constraint; +use crate::constraint::SimpleConstraint; #[derive(Debug, Clone)] pub struct DevConstraintSet { @@ -12,32 +10,29 @@ pub struct DevConstraintSet { #[derive(Debug, Clone)] pub struct Interval { - start: Constraint, - end: Constraint, + start: SimpleConstraint, + end: SimpleConstraint, } impl Interval { - pub fn new(start: Constraint, end: Constraint) -> Self { + pub fn new(start: SimpleConstraint, end: SimpleConstraint) -> Self { Self { start, end } } - pub fn get_start(&self) -> &Constraint { + pub fn get_start(&self) -> &SimpleConstraint { &self.start } - pub fn get_end(&self) -> &Constraint { + pub fn get_end(&self) -> &SimpleConstraint { &self.end } - pub fn from_zero() -> &'static Constraint { - static ZERO: OnceLock<Constraint> = OnceLock::new(); - ZERO.get_or_init(|| Constraint::new(">=".to_string(), "0.0.0.0-dev".to_string())) + pub fn from_zero() -> SimpleConstraint { + SimpleConstraint::new(">=".to_string(), "0.0.0.0-dev".to_string(), None) } - pub fn until_positive_infinity() -> &'static Constraint { - static POSITIVE_INFINITY: OnceLock<Constraint> = OnceLock::new(); - POSITIVE_INFINITY - .get_or_init(|| Constraint::new("<".to_string(), format!("{}.0.0.0", i64::MAX))) + pub fn until_positive_infinity() -> SimpleConstraint { + SimpleConstraint::new("<".to_string(), format!("{}.0.0.0", i64::MAX), None) } pub fn any() -> Self { diff --git a/crates/shirabe-semver/src/intervals.rs b/crates/shirabe-semver/src/intervals.rs index 72231b6..68b23bf 100644 --- a/crates/shirabe-semver/src/intervals.rs +++ b/crates/shirabe-semver/src/intervals.rs @@ -3,11 +3,11 @@ use std::collections::HashMap; use std::sync::{Mutex, OnceLock}; -use crate::constraint::Constraint; -use crate::constraint::ConstraintInterface; +use crate::constraint::AnyConstraint; use crate::constraint::MatchAllConstraint; use crate::constraint::MatchNoneConstraint; use crate::constraint::MultiConstraint; +use crate::constraint::SimpleConstraint; use crate::interval::{DevConstraintSet, Interval}; use shirabe_php_shim as php; @@ -42,31 +42,19 @@ impl Intervals { } pub fn is_subset_of( - candidate: &dyn ConstraintInterface, - constraint: &dyn ConstraintInterface, + candidate: &AnyConstraint, + constraint: &AnyConstraint, ) -> anyhow::Result<bool> { - if constraint - .as_any() - .downcast_ref::<MatchAllConstraint>() - .is_some() - { + if constraint.is_match_all() { return Ok(true); } - if candidate - .as_any() - .downcast_ref::<MatchNoneConstraint>() - .is_some() - || constraint - .as_any() - .downcast_ref::<MatchNoneConstraint>() - .is_some() - { + if candidate.is_match_none() || constraint.is_match_none() { return Ok(false); } - // Phase B: ConstraintInterface needs clone_box() to create owned copies from references. - let multi = MultiConstraint::new(vec![candidate.clone_box(), constraint.clone_box()], true); + let multi = + MultiConstraint::new(vec![candidate.clone(), constraint.clone()], true, None).into(); let intersection_intervals = Self::get(&multi)?; let candidate_intervals = Self::get(candidate)?; @@ -79,14 +67,14 @@ impl Intervals { return Ok(false); } - if candidate_intervals.numeric[index].get_start().__to_string() - != interval.get_start().__to_string() + if candidate_intervals.numeric[index].get_start().to_string() + != interval.get_start().to_string() { return Ok(false); } - if candidate_intervals.numeric[index].get_end().__to_string() - != interval.get_end().__to_string() + if candidate_intervals.numeric[index].get_end().to_string() + != interval.get_end().to_string() { return Ok(false); } @@ -107,24 +95,16 @@ impl Intervals { Ok(true) } - pub fn have_intersections( - a: &dyn ConstraintInterface, - b: &dyn ConstraintInterface, - ) -> anyhow::Result<bool> { - if a.as_any().downcast_ref::<MatchAllConstraint>().is_some() - || b.as_any().downcast_ref::<MatchAllConstraint>().is_some() - { + pub fn have_intersections(a: &AnyConstraint, b: &AnyConstraint) -> anyhow::Result<bool> { + if a.is_match_all() || b.is_match_all() { return Ok(true); } - if a.as_any().downcast_ref::<MatchNoneConstraint>().is_some() - || b.as_any().downcast_ref::<MatchNoneConstraint>().is_some() - { + if a.is_match_none() || b.is_match_none() { return Ok(false); } - // Phase B: ConstraintInterface needs clone_box(). - let multi = MultiConstraint::new(vec![a.clone_box(), b.clone_box()], true); + let multi = MultiConstraint::new(vec![a.clone(), b.clone()], true, None).into(); let intersection_intervals = Self::generate_intervals(&multi, true)?; Ok(!intersection_intervals.numeric.is_empty() @@ -132,30 +112,24 @@ impl Intervals { || !intersection_intervals.branches.names.is_empty()) } - pub fn compact_constraint( - constraint: &dyn ConstraintInterface, - ) -> anyhow::Result<Box<dyn ConstraintInterface>> { - if constraint - .as_any() - .downcast_ref::<MultiConstraint>() - .is_none() - { - return Ok(constraint.clone_box()); + pub fn compact_constraint(constraint: &AnyConstraint) -> anyhow::Result<AnyConstraint> { + if constraint.as_multi_constraint().is_none() { + return Ok(constraint.clone()); } let intervals = Self::generate_intervals(constraint, false)?; - let mut constraints: Vec<Box<dyn ConstraintInterface>> = Vec::new(); + let mut constraints: Vec<AnyConstraint> = Vec::new(); let mut has_numeric_match_all = false; if intervals.numeric.len() == 1 - && intervals.numeric[0].get_start().__to_string() == Interval::from_zero().__to_string() - && intervals.numeric[0].get_end().__to_string() - == Interval::until_positive_infinity().__to_string() + && intervals.numeric[0].get_start().to_string() == Interval::from_zero().to_string() + && intervals.numeric[0].get_end().to_string() + == Interval::until_positive_infinity().to_string() { - constraints.push(Box::new(intervals.numeric[0].get_start().clone())); + constraints.push(intervals.numeric[0].get_start().clone().into()); has_numeric_match_all = true; } else { - let mut un_equal_constraints: Vec<Box<dyn ConstraintInterface>> = Vec::new(); + let mut un_equal_constraints: Vec<AnyConstraint> = Vec::new(); let count = intervals.numeric.len(); let mut i = 0; while i < count { @@ -176,15 +150,18 @@ impl Intervals { // unEqualConstraints currently contains [>=M, !=N] already and we only // want to add !=P right now if un_equal_constraints.is_empty() - && interval.get_start().__to_string() - != Interval::from_zero().__to_string() + && interval.get_start().to_string() != Interval::from_zero().to_string() { - un_equal_constraints.push(Box::new(interval.get_start().clone())); + un_equal_constraints.push(interval.get_start().clone().into()); } - un_equal_constraints.push(Box::new(Constraint::new( - "!=".to_string(), - interval.get_end().get_version().to_string(), - ))); + un_equal_constraints.push( + SimpleConstraint::new( + "!=".to_string(), + interval.get_end().get_version().to_string(), + None, + ) + .into(), + ); i += 1; continue; } @@ -192,16 +169,16 @@ impl Intervals { if !un_equal_constraints.is_empty() { // this is where the end of the following interval of a != constraint is added - if interval.get_end().__to_string() - != Interval::until_positive_infinity().__to_string() + if interval.get_end().to_string() + != Interval::until_positive_infinity().to_string() { - un_equal_constraints.push(Box::new(interval.get_end().clone())); + un_equal_constraints.push(interval.get_end().clone().into()); } // count is 1 if entire constraint is just one != expression if un_equal_constraints.len() > 1 { constraints - .push(Box::new(MultiConstraint::new(un_equal_constraints, true))); + .push(MultiConstraint::new(un_equal_constraints, true, None).into()); } else { constraints.push(un_equal_constraints.into_iter().next().unwrap()); } @@ -216,55 +193,59 @@ impl Intervals { && interval.get_start().get_operator() == ">=" && interval.get_end().get_operator() == "<=" { - constraints.push(Box::new(Constraint::new( - "==".to_string(), - interval.get_start().get_version().to_string(), - ))); + constraints.push( + SimpleConstraint::new( + "==".to_string(), + interval.get_start().get_version().to_string(), + None, + ) + .into(), + ); i += 1; continue; } - if interval.get_start().__to_string() == Interval::from_zero().__to_string() { - constraints.push(Box::new(interval.get_end().clone())); - } else if interval.get_end().__to_string() - == Interval::until_positive_infinity().__to_string() + if interval.get_start().to_string() == Interval::from_zero().to_string() { + constraints.push(interval.get_end().clone().into()); + } else if interval.get_end().to_string() + == Interval::until_positive_infinity().to_string() { - constraints.push(Box::new(interval.get_start().clone())); + constraints.push(interval.get_start().clone().into()); } else { - constraints.push(Box::new(MultiConstraint::new( - vec![ - Box::new(interval.get_start().clone()), - Box::new(interval.get_end().clone()), - ], - true, - ))); + constraints.push( + MultiConstraint::new( + vec![ + AnyConstraint::Simple(interval.get_start().clone()), + AnyConstraint::Simple(interval.get_end().clone()), + ], + true, + None, + ) + .into(), + ); } i += 1; } } - let mut dev_constraints: Vec<Box<dyn ConstraintInterface>> = Vec::new(); + let mut dev_constraints: Vec<AnyConstraint> = Vec::new(); if intervals.branches.names.is_empty() { if intervals.branches.exclude && has_numeric_match_all { - return Ok(Box::new(MatchAllConstraint { - pretty_string: None, - })); + return Ok(MatchAllConstraint::new(None).into()); // otherwise constraint should contain a != operator and already cover this } } else { for branch_name in &intervals.branches.names { if intervals.branches.exclude { - dev_constraints.push(Box::new(Constraint::new( - "!=".to_string(), - branch_name.clone(), - ))); + dev_constraints.push( + SimpleConstraint::new("!=".to_string(), branch_name.clone(), None).into(), + ); } else { - dev_constraints.push(Box::new(Constraint::new( - "==".to_string(), - branch_name.clone(), - ))); + dev_constraints.push( + SimpleConstraint::new("==".to_string(), branch_name.clone(), None).into(), + ); } } @@ -272,26 +253,25 @@ impl Intervals { // > 2.0 != dev-foo must return a conjunctive constraint if intervals.branches.exclude { if constraints.len() > 1 { - let merged: Vec<Box<dyn ConstraintInterface>> = - std::iter::once(Box::new(MultiConstraint::new(constraints, false)) - as Box<dyn ConstraintInterface>) - .chain(dev_constraints) - .collect(); - return Ok(Box::new(MultiConstraint::new(merged, true))); + let merged: Vec<AnyConstraint> = + std::iter::once(MultiConstraint::new(constraints, false, None).into()) + .chain(dev_constraints) + .collect(); + return Ok(MultiConstraint::new(merged, true, None).into()); } if constraints.len() == 1 - && constraints[0].__to_string() == Interval::from_zero().__to_string() + && constraints[0].to_string() == Interval::from_zero().to_string() { if dev_constraints.len() > 1 { - return Ok(Box::new(MultiConstraint::new(dev_constraints, true))); + return Ok(MultiConstraint::new(dev_constraints, true, None).into()); } return Ok(dev_constraints.into_iter().next().unwrap()); } - let merged: Vec<Box<dyn ConstraintInterface>> = + let merged: Vec<AnyConstraint> = constraints.into_iter().chain(dev_constraints).collect(); - return Ok(Box::new(MultiConstraint::new(merged, true))); + return Ok(MultiConstraint::new(merged, true, None).into()); } // otherwise devConstraints contains a list of == operators for branches which are @@ -300,20 +280,18 @@ impl Intervals { } if constraints.len() > 1 { - return Ok(Box::new(MultiConstraint::new(constraints, false))); + return Ok(MultiConstraint::new(constraints, false, None).into()); } if constraints.len() == 1 { return Ok(constraints.into_iter().next().unwrap()); } - Ok(Box::new(MatchNoneConstraint { - pretty_string: None, - })) + Ok(MatchNoneConstraint::new(None).into()) } - pub fn get(constraint: &dyn ConstraintInterface) -> anyhow::Result<IntervalCollection> { - let key = constraint.__to_string(); + pub fn get(constraint: &AnyConstraint) -> anyhow::Result<IntervalCollection> { + let key = constraint.to_string(); { let cache = intervals_cache().lock().unwrap(); @@ -333,14 +311,10 @@ impl Intervals { } fn generate_intervals( - constraint: &dyn ConstraintInterface, + constraint: &AnyConstraint, stop_on_first_valid_interval: bool, ) -> anyhow::Result<IntervalCollection> { - if constraint - .as_any() - .downcast_ref::<MatchAllConstraint>() - .is_some() - { + if constraint.is_match_all() { return Ok(IntervalCollection { numeric: vec![Interval::new( Interval::from_zero().clone(), @@ -350,37 +324,30 @@ impl Intervals { }); } - if constraint - .as_any() - .downcast_ref::<MatchNoneConstraint>() - .is_some() - { + if constraint.is_match_none() { return Ok(IntervalCollection { numeric: vec![], branches: Interval::no_dev(), }); } - if let Some(c) = constraint.as_any().downcast_ref::<Constraint>() { + if let Some(c) = constraint.as_constraint() { return Self::generate_single_constraint_intervals(c); } - let multi = constraint - .as_any() - .downcast_ref::<MultiConstraint>() - .ok_or_else(|| { - anyhow::anyhow!( - "The constraint passed in should be an MatchAllConstraint, Constraint or \ + let multi = constraint.as_multi_constraint().ok_or_else(|| { + anyhow::anyhow!( + "The constraint passed in should be an MatchAllConstraint, Constraint or \ MultiConstraint instance, got an unknown type." - ) - })?; + ) + })?; let sub_constraints = multi.get_constraints(); let mut numeric_groups: Vec<Vec<Interval>> = Vec::new(); let mut constraint_branches: Vec<DevConstraintSet> = Vec::new(); for c in sub_constraints { - let res = Self::get(c.as_ref())?; + let res = Self::get(c)?; numeric_groups.push(res.numeric); constraint_branches.push(res.branches); } @@ -494,7 +461,7 @@ impl Intervals { } else { 1 }; - let mut start: Option<Constraint> = None; + let mut start: Option<SimpleConstraint> = None; for (version, operator, is_start) in &borders { if *is_start { @@ -504,7 +471,11 @@ impl Intervals { } if start.is_none() && active_intervals >= activation_threshold { - start = Some(Constraint::new(operator.clone(), version.clone())); + start = Some(SimpleConstraint::new( + operator.clone(), + version.clone(), + None, + )); } else if start.is_some() && active_intervals < activation_threshold { let start_c = start.take().unwrap(); // filter out invalid intervals like > x - <= x, or >= x - < x @@ -516,7 +487,7 @@ impl Intervals { } else { intervals.push(Interval::new( start_c, - Constraint::new(operator.clone(), version.clone()), + SimpleConstraint::new(operator.clone(), version.clone(), None), )); if stop_on_first_valid_interval { @@ -533,7 +504,7 @@ impl Intervals { } fn generate_single_constraint_intervals( - constraint: &Constraint, + constraint: &SimpleConstraint, ) -> anyhow::Result<IntervalCollection> { let op = constraint.get_operator(); @@ -589,10 +560,18 @@ impl Intervals { numeric: vec![ Interval::new( Interval::from_zero().clone(), - Constraint::new("<".to_string(), constraint.get_version().to_string()), + SimpleConstraint::new( + "<".to_string(), + constraint.get_version().to_string(), + None, + ), ), Interval::new( - Constraint::new(">".to_string(), constraint.get_version().to_string()), + SimpleConstraint::new( + ">".to_string(), + constraint.get_version().to_string(), + None, + ), Interval::until_positive_infinity().clone(), ), ], @@ -603,8 +582,8 @@ impl Intervals { // convert ==x to an interval of >=x - <=x Ok(IntervalCollection { numeric: vec![Interval::new( - Constraint::new(">=".to_string(), constraint.get_version().to_string()), - Constraint::new("<=".to_string(), constraint.get_version().to_string()), + SimpleConstraint::new(">=".to_string(), constraint.get_version().to_string(), None), + SimpleConstraint::new("<=".to_string(), constraint.get_version().to_string(), None), )], branches: Interval::no_dev(), }) diff --git a/crates/shirabe-semver/src/semver.rs b/crates/shirabe-semver/src/semver.rs index 62f50b6..2314069 100644 --- a/crates/shirabe-semver/src/semver.rs +++ b/crates/shirabe-semver/src/semver.rs @@ -3,7 +3,8 @@ use std::sync::OnceLock; use crate::comparator::Comparator; -use crate::constraint::Constraint; +use crate::constraint::AnyConstraint; +use crate::constraint::SimpleConstraint; use crate::version_parser::VersionParser; pub struct Semver; @@ -19,7 +20,12 @@ impl Semver { pub fn satisfies(version: String, constraints: String) -> anyhow::Result<bool> { let version_parser = Self::version_parser(); - let provider = Constraint::new("==".to_string(), version_parser.normalize(&version, None)?); + let provider = SimpleConstraint::new( + "==".to_string(), + version_parser.normalize(&version, None)?, + None, + ) + .into(); let parsed_constraints = version_parser.parse_constraints(&constraints)?; Ok(parsed_constraints.matches(&provider)) } diff --git a/crates/shirabe-semver/src/version_parser.rs b/crates/shirabe-semver/src/version_parser.rs index ba2b59d..bbd359e 100644 --- a/crates/shirabe-semver/src/version_parser.rs +++ b/crates/shirabe-semver/src/version_parser.rs @@ -1,9 +1,9 @@ //! ref: composer/vendor/composer/semver/src/VersionParser.php -use crate::constraint::Constraint; -use crate::constraint::ConstraintInterface; +use crate::constraint::AnyConstraint; use crate::constraint::MatchAllConstraint; use crate::constraint::MultiConstraint; +use crate::constraint::SimpleConstraint; use shirabe_php_shim as php; // Regex to match pre-release data (sort of). @@ -294,16 +294,13 @@ impl VersionParser { name.to_string() } - pub fn parse_constraints( - &self, - constraints: &str, - ) -> anyhow::Result<Box<dyn ConstraintInterface>> { + pub fn parse_constraints(&self, constraints: &str) -> anyhow::Result<AnyConstraint> { let pretty_constraint = constraints.to_string(); let or_constraints = php::preg_split("{\\s*\\|\\|?\\s*}", &php::trim(constraints, None)) .ok_or_else(|| anyhow::anyhow!("Failed to preg_split string: {}", constraints))?; - let mut or_groups: Vec<Box<dyn ConstraintInterface>> = Vec::new(); + let mut or_groups: Vec<AnyConstraint> = Vec::new(); for or_constraint in &or_constraints { let and_constraints = php::preg_split( @@ -312,9 +309,8 @@ impl VersionParser { ) .ok_or_else(|| anyhow::anyhow!("Failed to preg_split string: {}", or_constraint))?; - let constraint_objects: Vec<Box<dyn ConstraintInterface>> = if and_constraints.len() > 1 - { - let mut objs: Vec<Box<dyn ConstraintInterface>> = Vec::new(); + let constraint_objects: Vec<AnyConstraint> = if and_constraints.len() > 1 { + let mut objs: Vec<AnyConstraint> = Vec::new(); for and_constraint in &and_constraints { for parsed in self.parse_constraint(and_constraint)? { objs.push(parsed); @@ -325,26 +321,21 @@ impl VersionParser { self.parse_constraint(&and_constraints[0])? }; - let constraint: Box<dyn ConstraintInterface> = if constraint_objects.len() == 1 { + let constraint: AnyConstraint = if constraint_objects.len() == 1 { constraint_objects.into_iter().next().unwrap() } else { - Box::new(MultiConstraint::new(constraint_objects, true)) + MultiConstraint::new(constraint_objects, true, None).into() }; or_groups.push(constraint); } - let mut parsed_constraint = MultiConstraint::create(or_groups, false)?; - - parsed_constraint.set_pretty_string(Some(pretty_constraint)); - - Ok(parsed_constraint) + // PHP sets the pretty string on the create() result via setPrettyString(); + // the port threads it through create() instead (no setter). + MultiConstraint::create(or_groups, false, Some(pretty_constraint)) } - fn parse_constraint( - &self, - constraint: &str, - ) -> anyhow::Result<Vec<Box<dyn ConstraintInterface>>> { + fn parse_constraint(&self, constraint: &str) -> anyhow::Result<Vec<AnyConstraint>> { let mut constraint = constraint.to_string(); // strip off aliasing @@ -399,15 +390,14 @@ impl VersionParser { .unwrap_or("") .is_empty(); if m1_nonempty || m2_nonempty { - return Ok(vec![Box::new(Constraint::new( + return Ok(vec![AnyConstraint::Simple(SimpleConstraint::new( ">=".to_string(), "0.0.0.0-dev".to_string(), + None, ))]); } - return Ok(vec![Box::new(MatchAllConstraint { - pretty_string: None, - })]); + return Ok(vec![AnyConstraint::MatchAll(MatchAllConstraint::new(None))]); } let version_regex = format!( @@ -461,7 +451,7 @@ impl VersionParser { let low_version = self.normalize(&format!("{}{}", &constraint[1..], stability_suffix), None)?; - let lower_bound = Constraint::new(">=".to_string(), low_version); + let lower_bound = SimpleConstraint::new(">=".to_string(), low_version, None); // For upper bound, we increment the position of one more significance, // but highPosition = 0 would be illegal @@ -471,9 +461,12 @@ impl VersionParser { self.manipulate_version_string(&matches, high_position, 1, "0") .unwrap_or_default() ); - let upper_bound = Constraint::new("<".to_string(), high_version); + let upper_bound = SimpleConstraint::new("<".to_string(), high_version, None); - return Ok(vec![Box::new(lower_bound), Box::new(upper_bound)]); + return Ok(vec![ + AnyConstraint::Simple(lower_bound), + AnyConstraint::Simple(upper_bound), + ]); } // Caret Range @@ -508,7 +501,7 @@ impl VersionParser { let low_version = self.normalize(&format!("{}{}", &constraint[1..], stability_suffix), None)?; - let lower_bound = Constraint::new(">=".to_string(), low_version); + let lower_bound = SimpleConstraint::new(">=".to_string(), low_version, None); // For upper bound, we increment the position of one more significance, // but highPosition = 0 would be illegal @@ -517,9 +510,12 @@ impl VersionParser { self.manipulate_version_string(&matches, position, 1, "0") .unwrap_or_default() ); - let upper_bound = Constraint::new("<".to_string(), high_version); + let upper_bound = SimpleConstraint::new("<".to_string(), high_version, None); - return Ok(vec![Box::new(lower_bound), Box::new(upper_bound)]); + return Ok(vec![ + AnyConstraint::Simple(lower_bound), + AnyConstraint::Simple(upper_bound), + ]); } // X Range @@ -554,15 +550,16 @@ impl VersionParser { ); if low_version == "0.0.0.0-dev" { - return Ok(vec![Box::new(Constraint::new( + return Ok(vec![AnyConstraint::Simple(SimpleConstraint::new( "<".to_string(), high_version, + None, ))]); } return Ok(vec![ - Box::new(Constraint::new(">=".to_string(), low_version)), - Box::new(Constraint::new("<".to_string(), high_version)), + AnyConstraint::Simple(SimpleConstraint::new(">=".to_string(), low_version, None)), + AnyConstraint::Simple(SimpleConstraint::new("<".to_string(), high_version, None)), ]); } @@ -593,9 +590,10 @@ impl VersionParser { let from_str = matches[1].clone().unwrap_or_default(); // matches['from'] let low_version = self.normalize(&from_str, None)?; - let lower_bound = Constraint::new( + let lower_bound = SimpleConstraint::new( ">=".to_string(), format!("{}{}", low_version, low_stability_suffix), + None, ); // PHP's empty() on "0" returns true, but here we only check for truly empty/missing @@ -603,14 +601,14 @@ impl VersionParser { // matches[12]=to minor, matches[13]=to patch, matches[15]=to stability, // matches[17]=to dev, matches[18]=to wildcard-dev - let upper_bound: Constraint = if (!empty(&matches[12]) && !empty(&matches[13])) + let upper_bound: SimpleConstraint = if (!empty(&matches[12]) && !empty(&matches[13])) || !matches[15].as_deref().unwrap_or("").is_empty() || !matches[17].as_deref().unwrap_or("").is_empty() || !matches[18].as_deref().unwrap_or("").is_empty() { let to_str = matches[10].clone().unwrap_or_default(); // matches['to'] let hv = self.normalize(&to_str, None)?; - Constraint::new("<=".to_string(), hv) + SimpleConstraint::new("<=".to_string(), hv, None) } else { // matches[11]=to major, matches[12]=to minor, matches[13]=to patch, // matches[14]=to fourth @@ -632,10 +630,13 @@ impl VersionParser { self.manipulate_version_string(&high_match, position, 1, "0") .unwrap_or_default() ); - Constraint::new("<".to_string(), hv) + SimpleConstraint::new("<".to_string(), hv, None) }; - return Ok(vec![Box::new(lower_bound), Box::new(upper_bound)]); + return Ok(vec![ + AnyConstraint::Simple(lower_bound), + AnyConstraint::Simple(upper_bound), + ]); } // Basic Comparators @@ -691,7 +692,9 @@ impl VersionParser { } else { op_str }; - return Ok(vec![Box::new(Constraint::new(final_op, version))]); + return Ok(vec![AnyConstraint::Simple(SimpleConstraint::new( + final_op, version, None, + ))]); } } diff --git a/crates/shirabe/src/advisory/ignored_security_advisory.rs b/crates/shirabe/src/advisory/ignored_security_advisory.rs index 1cd5f75..140c00b 100644 --- a/crates/shirabe/src/advisory/ignored_security_advisory.rs +++ b/crates/shirabe/src/advisory/ignored_security_advisory.rs @@ -4,7 +4,7 @@ use crate::advisory::SecurityAdvisory; use chrono::{DateTime, Utc}; use indexmap::IndexMap; use shirabe_php_shim::PhpMixed; -use shirabe_semver::constraint::ConstraintInterface; +use shirabe_semver::constraint::AnyConstraint; #[derive(Debug, serde::Serialize)] #[serde(rename_all = "camelCase")] @@ -19,7 +19,7 @@ impl IgnoredSecurityAdvisory { pub fn new( package_name: String, advisory_id: String, - affected_versions: Box<dyn ConstraintInterface>, + affected_versions: AnyConstraint, title: String, sources: Vec<IndexMap<String, String>>, reported_at: DateTime<Utc>, diff --git a/crates/shirabe/src/advisory/partial_security_advisory.rs b/crates/shirabe/src/advisory/partial_security_advisory.rs index 6953ef3..fe3c99e 100644 --- a/crates/shirabe/src/advisory/partial_security_advisory.rs +++ b/crates/shirabe/src/advisory/partial_security_advisory.rs @@ -7,12 +7,12 @@ use chrono::{DateTime, TimeZone, Utc}; use indexmap::IndexMap; use shirabe_external_packages::composer::pcre::Preg; use shirabe_php_shim::{PhpMixed, UnexpectedValueException}; -use shirabe_semver::constraint::Constraint; -use shirabe_semver::constraint::ConstraintInterface; +use shirabe_semver::constraint::AnyConstraint; +use shirabe_semver::constraint::SimpleConstraint; use shirabe_semver::version_parser::VersionParser; fn serialize_constraint<S: serde::Serializer>( - c: &Box<dyn ConstraintInterface>, + c: &AnyConstraint, serializer: S, ) -> Result<S::Ok, S::Error> { serializer.serialize_str(&c.get_pretty_string()) @@ -24,7 +24,7 @@ pub struct PartialSecurityAdvisory { pub advisory_id: String, pub package_name: String, #[serde(serialize_with = "serialize_constraint")] - pub affected_versions: Box<dyn ConstraintInterface>, + pub affected_versions: AnyConstraint, } impl PartialSecurityAdvisory { @@ -35,21 +35,22 @@ impl PartialSecurityAdvisory { ) -> Result<PartialOrSecurityAdvisory> { let affected_versions_str = data["affectedVersions"].as_string().unwrap_or(""); - let constraint: Box<dyn ConstraintInterface> = - match parser.parse_constraints(affected_versions_str) { - Ok(c) => c, - Err(_) => { - let affected_version = - Preg::replace(r"(^[>=<^~]*[\d.]+).*", "$1", affected_versions_str); - match parser.parse_constraints(affected_version.as_deref().unwrap_or("")) { - Ok(c) => c, - Err(_) => Box::new(Constraint::new( - "==".to_string(), - "0.0.0-invalid-version".to_string(), - )), - } + let constraint: AnyConstraint = match parser.parse_constraints(affected_versions_str) { + Ok(c) => c, + Err(_) => { + let affected_version = + Preg::replace(r"(^[>=<^~]*[\d.]+).*", "$1", affected_versions_str); + match parser.parse_constraints(affected_version.as_deref().unwrap_or("")) { + Ok(c) => c, + Err(_) => SimpleConstraint::new( + "==".to_string(), + "0.0.0-invalid-version".to_string(), + None, + ) + .into(), } - }; + } + }; let has_full_data = data.contains_key("title") && data.contains_key("sources") @@ -93,7 +94,7 @@ impl PartialSecurityAdvisory { pub fn new( package_name: String, advisory_id: String, - affected_versions: Box<dyn ConstraintInterface>, + affected_versions: AnyConstraint, ) -> Self { Self { advisory_id, diff --git a/crates/shirabe/src/advisory/security_advisory.rs b/crates/shirabe/src/advisory/security_advisory.rs index 9787d37..edb161c 100644 --- a/crates/shirabe/src/advisory/security_advisory.rs +++ b/crates/shirabe/src/advisory/security_advisory.rs @@ -2,7 +2,7 @@ use chrono::{DateTime, Utc}; use indexmap::IndexMap; -use shirabe_semver::constraint::ConstraintInterface; +use shirabe_semver::constraint::AnyConstraint; use crate::advisory::IgnoredSecurityAdvisory; use crate::advisory::PartialSecurityAdvisory; @@ -24,7 +24,7 @@ impl SecurityAdvisory { pub fn new( package_name: String, advisory_id: String, - affected_versions: Box<dyn ConstraintInterface>, + affected_versions: AnyConstraint, title: String, sources: Vec<IndexMap<String, String>>, reported_at: DateTime<Utc>, @@ -48,15 +48,15 @@ impl SecurityAdvisory { &self.inner.advisory_id } - pub fn affected_versions(&self) -> &dyn ConstraintInterface { - &*self.inner.affected_versions + pub fn affected_versions(&self) -> &AnyConstraint { + &self.inner.affected_versions } pub fn to_ignored_advisory(&self, ignore_reason: Option<String>) -> IgnoredSecurityAdvisory { IgnoredSecurityAdvisory::new( self.inner.package_name.clone(), self.inner.advisory_id.clone(), - self.inner.affected_versions.clone_box(), + self.inner.affected_versions.clone(), self.title.clone(), self.sources.clone(), self.reported_at, diff --git a/crates/shirabe/src/autoload/autoload_generator.rs b/crates/shirabe/src/autoload/autoload_generator.rs index 7b7980a..5c9f008 100644 --- a/crates/shirabe/src/autoload/autoload_generator.rs +++ b/crates/shirabe/src/autoload/autoload_generator.rs @@ -14,8 +14,8 @@ use shirabe_php_shim::{ sprintf, str_contains, str_replace, str_starts_with, strlen, strpos, strtr, substr, substr_count, trigger_error, trim, unlink, var_export, }; +use shirabe_semver::constraint::AnyConstraint; use shirabe_semver::constraint::Bound; -use shirabe_semver::constraint::ConstraintInterface; use crate::autoload::ClassLoader; use crate::config::Config; @@ -1092,7 +1092,7 @@ impl AutoloadGenerator { let mut required_extensions: IndexMap<String, String> = IndexMap::new(); let mut extension_providers: IndexMap< String, - Vec<Box<dyn shirabe_semver::constraint::ConstraintInterface>>, + Vec<shirabe_semver::constraint::AnyConstraint>, > = IndexMap::new(); for item in package_map { @@ -1110,7 +1110,7 @@ impl AutoloadGenerator { extension_providers .entry(ext) .or_insert_with(Vec::new) - .push(link.get_constraint().clone_box()); + .push(link.get_constraint().clone()); } } } diff --git a/crates/shirabe/src/command/archive_command.rs b/crates/shirabe/src/command/archive_command.rs index c88b47b..0813df6 100644 --- a/crates/shirabe/src/command/archive_command.rs +++ b/crates/shirabe/src/command/archive_command.rs @@ -305,11 +305,11 @@ impl ArchiveCommand { ); repo_set.add_repository(Box::new(repo))?; let parser = VersionParser::new(); - let constraint: Option<Box<dyn shirabe_semver::constraint::ConstraintInterface>> = - match version.as_deref() { - Some(v) => Some(parser.parse_constraints(v)?.clone_box()), - None => None, - }; + let constraint: Option<shirabe_semver::constraint::AnyConstraint> = match version.as_deref() + { + Some(v) => Some(parser.parse_constraints(v)?.clone()), + None => None, + }; let packages = repo_set.find_packages(&package_name.to_lowercase(), constraint, 0); let package = if packages.len() > 1 { diff --git a/crates/shirabe/src/command/base_dependency_command.rs b/crates/shirabe/src/command/base_dependency_command.rs index 498c589..f1ff863 100644 --- a/crates/shirabe/src/command/base_dependency_command.rs +++ b/crates/shirabe/src/command/base_dependency_command.rs @@ -6,8 +6,8 @@ use shirabe_external_packages::symfony::component::console::input::InputInterfac use shirabe_external_packages::symfony::component::console::output::OutputInterface; use shirabe_external_packages::symfony::console::formatter::OutputFormatter; use shirabe_php_shim::{InvalidArgumentException, PhpMixed, UnexpectedValueException}; +use shirabe_semver::constraint::AnyConstraint; use shirabe_semver::constraint::Bound; -use shirabe_semver::constraint::ConstraintInterface; use crate::command::{BaseCommand, BaseCommandData, HasBaseCommandData}; use crate::package::CompletePackageInterface; @@ -225,13 +225,9 @@ pub trait BaseDependencyCommand: BaseCommand { } let has_constraint = text_constraint != "*"; - let constraint: Option<Box<dyn ConstraintInterface>> = if has_constraint { + let constraint: Option<AnyConstraint> = if has_constraint { let version_parser = VersionParser::new(); - Some( - version_parser - .parse_constraints(&text_constraint)? - .clone_box(), - ) + Some(version_parser.parse_constraints(&text_constraint)?.clone()) } else { None }; diff --git a/crates/shirabe/src/command/check_platform_reqs_command.rs b/crates/shirabe/src/command/check_platform_reqs_command.rs index 83770be..74ecfc3 100644 --- a/crates/shirabe/src/command/check_platform_reqs_command.rs +++ b/crates/shirabe/src/command/check_platform_reqs_command.rs @@ -5,8 +5,8 @@ use indexmap::IndexMap; use shirabe_external_packages::symfony::component::console::input::InputInterface; use shirabe_external_packages::symfony::component::console::output::OutputInterface; use shirabe_php_shim::{PhpMixed, strip_tags}; -use shirabe_semver::constraint::Constraint; -use shirabe_semver::constraint::ConstraintInterface; +use shirabe_semver::constraint::AnyConstraint; +use shirabe_semver::constraint::SimpleConstraint; use crate::command::{BaseCommand, BaseCommandData, HasBaseCommandData}; use crate::console::input::InputOption; @@ -147,22 +147,23 @@ impl CheckPlatformReqsCommand { if !candidates.is_empty() { let mut req_results: Vec<CheckResult> = vec![]; 'candidates: for candidate in &candidates { - let candidate_constraint: Option<Box<dyn ConstraintInterface>> = + let candidate_constraint: Option<AnyConstraint> = if candidate.get_name() == require { - let mut c = Constraint::new("=", candidate.get_version()); - c.set_pretty_string(Some( - candidate.get_pretty_version().to_string(), - )); - Some(Box::new(c)) + let c = SimpleConstraint::new( + "=".to_string(), + candidate.get_version().to_string(), + Some(candidate.get_pretty_version().to_string()), + ); + Some(c.into()) } else { - let mut found: Option<Box<dyn ConstraintInterface>> = None; + let mut found: Option<AnyConstraint> = None; for (_, link) in candidate .get_provides() .iter() .chain(candidate.get_replaces().iter()) { if link.get_target() == require { - found = Some(link.get_constraint().clone_box()); + found = Some(link.get_constraint().clone()); break; } } @@ -175,7 +176,7 @@ impl CheckPlatformReqsCommand { }; for link in links { - if !link.get_constraint().matches(&*candidate_constraint) { + if !link.get_constraint().matches(&candidate_constraint) { req_results.push(CheckResult { platform_package: if candidate.get_name() == require { candidate.get_pretty_name().to_string() diff --git a/crates/shirabe/src/command/fund_command.rs b/crates/shirabe/src/command/fund_command.rs index 233eeaa..82c0c0a 100644 --- a/crates/shirabe/src/command/fund_command.rs +++ b/crates/shirabe/src/command/fund_command.rs @@ -9,7 +9,7 @@ use shirabe_external_packages::symfony::component::console::input::InputInterfac use shirabe_external_packages::symfony::component::console::output::OutputInterface; use shirabe_external_packages::symfony::console::formatter::OutputFormatter; use shirabe_php_shim::PhpMixed; -use shirabe_semver::constraint::ConstraintInterface; +use shirabe_semver::constraint::AnyConstraint; use shirabe_semver::constraint::MatchAllConstraint; use crate::command::{BaseCommand, BaseCommandData, HasBaseCommandData}; @@ -64,8 +64,7 @@ impl FundCommand { ); let mut fundings: IndexMap<String, IndexMap<String, Vec<String>>> = IndexMap::new(); - let mut packages_to_load: IndexMap<String, Option<Box<dyn ConstraintInterface>>> = - IndexMap::new(); + let mut packages_to_load: IndexMap<String, Option<AnyConstraint>> = IndexMap::new(); let mut packages_to_load_names: indexmap::IndexSet<String> = indexmap::IndexSet::new(); for package in repo.get_packages() { if package.as_any().downcast_ref::<AliasPackage>().is_some() { @@ -73,7 +72,7 @@ impl FundCommand { } packages_to_load.insert( package.get_name().to_string(), - Some(Box::new(MatchAllConstraint::new())), + Some(MatchAllConstraint::new(None).into()), ); packages_to_load_names.insert(package.get_name().to_string()); } diff --git a/crates/shirabe/src/command/package_discovery_trait.rs b/crates/shirabe/src/command/package_discovery_trait.rs index 43d6ee8..fa25fa3 100644 --- a/crates/shirabe/src/command/package_discovery_trait.rs +++ b/crates/shirabe/src/command/package_discovery_trait.rs @@ -880,9 +880,12 @@ pub trait PackageDiscoveryTrait { }; if !link .get_constraint() - .matches(&shirabe_semver::constraint::Constraint::new( - "==", - platform_pkg.get_version(), + .matches(&shirabe_semver::constraint::AnyConstraint::Simple( + shirabe_semver::constraint::SimpleConstraint::new( + "==".to_string(), + platform_pkg.get_version().to_string(), + None, + ), )) { let mut platform_pkg_version = platform_pkg.get_pretty_version().to_string(); diff --git a/crates/shirabe/src/command/show_command.rs b/crates/shirabe/src/command/show_command.rs index 3eac2df..8826421 100644 --- a/crates/shirabe/src/command/show_command.rs +++ b/crates/shirabe/src/command/show_command.rs @@ -13,7 +13,7 @@ use shirabe_php_shim::{ date, extension_loaded, in_array, realpath, strtolower, version_compare, }; -use shirabe_semver::constraint::ConstraintInterface; +use shirabe_semver::constraint::AnyConstraint; use crate::command::{BaseCommand, BaseCommandData, HasBaseCommandData}; use crate::composer::PartialComposerHandle; @@ -1486,7 +1486,7 @@ impl ShowCommand { IndexMap<String, String>, )> { let name = strtolower(name); - let constraint: Option<Box<dyn ConstraintInterface>> = match &version { + let constraint: Option<AnyConstraint> = match &version { PhpMixed::String(s) => Some(self.version_parser.parse_constraints(s)?), PhpMixed::Null => None, _ => None, // already a ConstraintInterface @@ -1514,7 +1514,7 @@ impl ShowCommand { } else { repository_set.create_pool_for_package(&name, None)? }; - let matches = pool.what_provides(&name, constraint.as_deref()); + let matches = pool.what_provides(&name, constraint.as_ref()); let mut literals: Vec<i64> = Vec::new(); for package in matches.iter() { // avoid showing the 9999999-dev alias if the default branch has no branch-alias set diff --git a/crates/shirabe/src/command/update_command.rs b/crates/shirabe/src/command/update_command.rs index c4ae890..4096272 100644 --- a/crates/shirabe/src/command/update_command.rs +++ b/crates/shirabe/src/command/update_command.rs @@ -178,9 +178,7 @@ impl UpdateCommand { for (package, constraint) in &reqs { let package = strtolower(package); let parsed_constraint = parser.parse_constraints(constraint)?; - // TODO(phase-b): clone_box because Box<dyn ConstraintInterface> isn't Clone. - temporary_constraints.insert(package.clone(), parsed_constraint.clone_box()); - let _ = parsed_constraint; + temporary_constraints.insert(package.clone(), parsed_constraint); // TODO(phase-b): access root_requirements[package].getConstraint() let intersected: bool = todo!("Intervals::haveIntersections check"); if let Some(_root_req) = todo!("root_requirements.get(&package)") as Option<PhpMixed> { @@ -234,10 +232,9 @@ impl UpdateCommand { matches.get(1).cloned().unwrap_or_default() ))?; if temporary_constraints.contains_key(package.get_name()) { - // TODO(phase-b): Box<dyn ConstraintInterface> isn't Clone; clone_box workaround. let existing = temporary_constraints .get(package.get_name()) - .map(|c| c.clone_box()) + .map(|c| c.clone()) .unwrap(); temporary_constraints.insert( package.get_name().to_string(), diff --git a/crates/shirabe/src/dependency_resolver/default_policy.rs b/crates/shirabe/src/dependency_resolver/default_policy.rs index c24e382..cc33b81 100644 --- a/crates/shirabe/src/dependency_resolver/default_policy.rs +++ b/crates/shirabe/src/dependency_resolver/default_policy.rs @@ -5,7 +5,8 @@ use std::cell::RefCell; use indexmap::IndexMap; use shirabe_semver::compiling_matcher::CompilingMatcher; -use shirabe_semver::constraint::Constraint; +use shirabe_semver::constraint::AnyConstraint; +use shirabe_semver::constraint::SimpleConstraint; use crate::dependency_resolver::PolicyInterface; use crate::dependency_resolver::Pool; @@ -209,14 +210,16 @@ impl PolicyInterface for DefaultPolicy { if (a.is_dev() && a.get_version().starts_with("dev-")) || (b.is_dev() && b.get_version().starts_with("dev-")) { - let constraint = Constraint::new(operator, b.get_version()); - let version = Constraint::new("==", a.get_version()); + let constraint = + SimpleConstraint::new(operator.to_string(), b.get_version().to_string(), None); + let version = + SimpleConstraint::new("==".to_string(), a.get_version().to_string(), None); return constraint.match_specific(&version, true); } CompilingMatcher::r#match( - &Constraint::new(operator, b.get_version()), - Constraint::OP_EQ, + &SimpleConstraint::new(operator.to_string(), b.get_version().to_string(), None).into(), + SimpleConstraint::OP_EQ, a.get_version().to_string(), ) } diff --git a/crates/shirabe/src/dependency_resolver/pool.rs b/crates/shirabe/src/dependency_resolver/pool.rs index e43eb53..771f363 100644 --- a/crates/shirabe/src/dependency_resolver/pool.rs +++ b/crates/shirabe/src/dependency_resolver/pool.rs @@ -5,8 +5,8 @@ use std::fmt; use indexmap::IndexMap; use shirabe_php_shim::{Countable, STR_PAD_LEFT, abs, spl_object_hash, str_pad}; use shirabe_semver::compiling_matcher::CompilingMatcher; -use shirabe_semver::constraint::Constraint; -use shirabe_semver::constraint::ConstraintInterface; +use shirabe_semver::constraint::AnyConstraint; +use shirabe_semver::constraint::SimpleConstraint; use crate::advisory::PartialSecurityAdvisory; use crate::package::BasePackage; @@ -70,7 +70,7 @@ impl Pool { pub fn get_removed_versions( &self, name: &str, - constraint: &dyn ConstraintInterface, + constraint: &AnyConstraint, ) -> IndexMap<String, String> { let Some(versions) = self.removed_versions.get(name) else { return IndexMap::new(); @@ -78,7 +78,9 @@ impl Pool { let mut result: IndexMap<String, String> = IndexMap::new(); for (version, pretty_version) in versions { - if constraint.matches(&Constraint::new("==", version)) { + if constraint + .matches(&SimpleConstraint::new("==".to_string(), version.to_string(), None).into()) + { result.insert(version.clone(), pretty_version.clone()); } } @@ -110,7 +112,7 @@ impl Pool { pub fn is_security_removed_package_version( &self, package_name: &str, - constraint: Option<&dyn ConstraintInterface>, + constraint: Option<&AnyConstraint>, ) -> bool { let empty = IndexMap::new(); let versions = self @@ -119,7 +121,9 @@ impl Pool { .unwrap_or(&empty); for (version, _package_with_security_advisories) in versions { if let Some(c) = constraint { - if c.matches(&Constraint::new("==", version)) { + if c.matches( + &SimpleConstraint::new("==".to_string(), version.to_string(), None).into(), + ) { return true; } } @@ -132,7 +136,7 @@ impl Pool { pub fn get_security_advisory_identifiers_for_package_version( &self, package_name: &str, - constraint: Option<&dyn ConstraintInterface>, + constraint: Option<&AnyConstraint>, ) -> Vec<String> { let empty = IndexMap::new(); let versions = self @@ -141,7 +145,9 @@ impl Pool { .unwrap_or(&empty); for (version, package_with_security_advisories) in versions { if let Some(c) = constraint { - if c.matches(&Constraint::new("==", version)) { + if c.matches( + &SimpleConstraint::new("==".to_string(), version.to_string(), None).into(), + ) { return package_with_security_advisories .iter() .map(|advisory| advisory.advisory_id.clone()) @@ -156,7 +162,7 @@ impl Pool { pub fn is_abandoned_removed_package_version( &self, package_name: &str, - constraint: Option<&dyn ConstraintInterface>, + constraint: Option<&AnyConstraint>, ) -> bool { let empty = IndexMap::new(); let versions = self @@ -165,7 +171,9 @@ impl Pool { .unwrap_or(&empty); for (version, _pretty_version) in versions { if let Some(c) = constraint { - if c.matches(&Constraint::new("==", version)) { + if c.matches( + &SimpleConstraint::new("==".to_string(), version.to_string(), None).into(), + ) { return true; } } @@ -226,11 +234,11 @@ impl Pool { pub fn what_provides( &mut self, name: &str, - constraint: Option<&dyn ConstraintInterface>, + constraint: Option<&AnyConstraint>, ) -> Vec<Box<dyn BasePackage>> { // PHP: $key = (string) $constraint; let key = match constraint { - Some(c) => c.__to_string(), + Some(c) => c.to_string(), None => String::new(), }; if let Some(by_key) = self.provider_cache.get(name) { @@ -254,7 +262,7 @@ impl Pool { pub(crate) fn compute_what_provides( &self, name: &str, - constraint: Option<&dyn ConstraintInterface>, + constraint: Option<&AnyConstraint>, ) -> Vec<Box<dyn BasePackage>> { let Some(candidates) = self.package_by_name.get(name) else { return vec![]; @@ -306,7 +314,7 @@ impl Pool { &self, candidate: &dyn BasePackage, name: &str, - constraint: Option<&dyn ConstraintInterface>, + constraint: Option<&AnyConstraint>, ) -> bool { let candidate_name = candidate.get_name(); let candidate_version = candidate.get_version(); @@ -315,7 +323,7 @@ impl Pool { return constraint.is_none() || CompilingMatcher::r#match( constraint.unwrap(), - Constraint::OP_EQ, + SimpleConstraint::OP_EQ, candidate_version.to_string(), ); } diff --git a/crates/shirabe/src/dependency_resolver/pool_builder.rs b/crates/shirabe/src/dependency_resolver/pool_builder.rs index fcc0339..f80ef38 100644 --- a/crates/shirabe/src/dependency_resolver/pool_builder.rs +++ b/crates/shirabe/src/dependency_resolver/pool_builder.rs @@ -11,10 +11,10 @@ use shirabe_php_shim::{ array_search, array_search_mixed, count, in_array, microtime, number_format, round, spl_object_hash, sprintf, strpos, }; -use shirabe_semver::constraint::Constraint; -use shirabe_semver::constraint::ConstraintInterface; +use shirabe_semver::constraint::AnyConstraint; use shirabe_semver::constraint::MatchAllConstraint; use shirabe_semver::constraint::MultiConstraint; +use shirabe_semver::constraint::SimpleConstraint; use crate::dependency_resolver::Pool; use crate::dependency_resolver::PoolOptimizer; @@ -41,13 +41,13 @@ pub struct PoolBuilder { stability_flags: IndexMap<String, i64>, root_aliases: IndexMap<String, IndexMap<String, IndexMap<String, String>>>, root_references: IndexMap<String, String>, - temporary_constraints: IndexMap<String, Box<dyn ConstraintInterface>>, + temporary_constraints: IndexMap<String, AnyConstraint>, event_dispatcher: Option<std::rc::Rc<std::cell::RefCell<EventDispatcher>>>, pool_optimizer: Option<PoolOptimizer>, io: Box<dyn IOInterface>, alias_map: IndexMap<String, IndexMap<i64, AliasPackage>>, - packages_to_load: IndexMap<String, Box<dyn ConstraintInterface>>, - loaded_packages: IndexMap<String, Box<dyn ConstraintInterface>>, + packages_to_load: IndexMap<String, AnyConstraint>, + loaded_packages: IndexMap<String, AnyConstraint>, loaded_per_repo: IndexMap<i64, IndexMap<String, IndexMap<String, Box<dyn PackageInterface>>>>, packages: IndexMap<i64, Box<dyn BasePackage>>, unacceptable_fixed_or_locked_packages: Vec<Box<dyn BasePackage>>, @@ -88,7 +88,7 @@ impl PoolBuilder { io: Box<dyn IOInterface>, event_dispatcher: Option<std::rc::Rc<std::cell::RefCell<EventDispatcher>>>, pool_optimizer: Option<PoolOptimizer>, - temporary_constraints: IndexMap<String, Box<dyn ConstraintInterface>>, + temporary_constraints: IndexMap<String, AnyConstraint>, security_advisory_pool_filter: Option<SecurityAdvisoryPoolFilter>, ) -> Self { Self { @@ -195,14 +195,14 @@ impl PoolBuilder { // loading any packages self.loaded_packages.insert( package.get_name().to_string(), - Box::new(MatchAllConstraint::new()), + MatchAllConstraint::new(None).into(), ); // replace means conflict, so if a fixed package replaces a name, no need to load that one, packages would conflict anyways for (_k, link) in &package.get_replaces() { self.loaded_packages.insert( link.get_target().to_string(), - Box::new(MatchAllConstraint::new()), + MatchAllConstraint::new(None).into(), ); } @@ -238,7 +238,7 @@ impl PoolBuilder { } self.packages_to_load - .insert(package_name.clone(), constraint.clone_box()); + .insert(package_name.clone(), constraint.clone()); self.max_extended_reqs.insert(package_name.clone(), true); } @@ -271,7 +271,7 @@ impl PoolBuilder { for package_name in package.get_names(true) { let constraint = match self.temporary_constraints.get(&package_name) { - Some(c) => c.clone_box(), + Some(c) => c.clone(), None => continue, }; @@ -288,7 +288,8 @@ impl PoolBuilder { let mut found = false; for (_idx, version) in &package_and_aliases { - if CompilingMatcher::matches(&*constraint, Constraint::OP_EQ, version) { + if CompilingMatcher::matches(&constraint, SimpleConstraint::OP_EQ, version) + { found = true; } } @@ -377,9 +378,9 @@ impl PoolBuilder { &mut self, request: &Request, name: &str, - constraint: &dyn ConstraintInterface, + constraint: &AnyConstraint, ) { - let constraint = constraint.clone_box(); + let constraint = constraint.clone(); // Skip platform requires at this stage if PlatformRepository::is_platform_package(name) { return; @@ -398,8 +399,8 @@ impl PoolBuilder { let root_requires = request.get_requires(); let mut constraint = constraint; if let Some(root_constraint) = root_requires.get(name) { - if !Intervals::is_subset_of(&*constraint, &**root_constraint).unwrap_or(false) { - constraint = root_constraint.clone_box(); + if !Intervals::is_subset_of(&constraint, root_constraint).unwrap_or(false) { + constraint = root_constraint.clone(); } } @@ -410,17 +411,18 @@ impl PoolBuilder { // MultiConstraint::create() will optimize anyway) if let Some(existing) = self.packages_to_load.get(name) { // Already marked for loading and this does not expand the constraint to be loaded, nothing to do - if Intervals::is_subset_of(&*constraint, &**existing).unwrap_or(false) { + if Intervals::is_subset_of(&constraint, existing).unwrap_or(false) { return; } // extend the constraint to be loaded constraint = Intervals::compact_constraint( MultiConstraint::create( - vec![existing.clone_box(), constraint.clone_box()], + vec![existing.clone(), constraint.clone()], false, + None, ) - .unwrap_or_else(|_| Box::new(MatchAllConstraint::new())), + .unwrap_or_else(|_| MatchAllConstraint::new(None).into()), ); } @@ -431,7 +433,7 @@ impl PoolBuilder { // No need to load this package with this constraint because it is // a subset of the constraint with which we have already loaded packages - if Intervals::is_subset_of(&*constraint, &**self.loaded_packages.get(name).unwrap()) + if Intervals::is_subset_of(&constraint, self.loaded_packages.get(name).unwrap()) .unwrap_or(false) { return; @@ -444,13 +446,11 @@ impl PoolBuilder { name.to_string(), Intervals::compact_constraint( MultiConstraint::create( - vec![ - self.loaded_packages.get(name).unwrap().clone_box(), - constraint, - ], + vec![self.loaded_packages.get(name).unwrap().clone(), constraint], false, + None, ) - .unwrap_or_else(|_| Box::new(MatchAllConstraint::new())), + .unwrap_or_else(|_| MatchAllConstraint::new(None).into()), ), ); self.loaded_packages.shift_remove(name); @@ -475,23 +475,23 @@ impl PoolBuilder { for name in to_remove { self.packages_to_load.shift_remove(&name); } - let snapshot: Vec<(String, Box<dyn ConstraintInterface>)> = self + let snapshot: Vec<(String, AnyConstraint)> = self .packages_to_load .iter() - .map(|(k, v)| (k.clone(), v.clone_box())) + .map(|(k, v)| (k.clone(), v.clone())) .collect(); for (name, constraint) in &snapshot { self.loaded_packages - .insert(name.clone(), constraint.clone_box()); + .insert(name.clone(), constraint.clone()); } // Load packages in chunks of 50 to prevent memory usage build-up due to caches of all sorts // TODO(phase-b): array_chunk shim signature expects &[T]; build IndexMap chunks manually. - let mut package_batches: Vec<IndexMap<String, Box<dyn ConstraintInterface>>> = { - let mut chunks: Vec<IndexMap<String, Box<dyn ConstraintInterface>>> = Vec::new(); - let mut current: IndexMap<String, Box<dyn ConstraintInterface>> = IndexMap::new(); + let mut package_batches: Vec<IndexMap<String, AnyConstraint>> = { + let mut chunks: Vec<IndexMap<String, AnyConstraint>> = Vec::new(); + let mut current: IndexMap<String, AnyConstraint> = IndexMap::new(); for (k, v) in self.packages_to_load.iter() { - current.insert(k.clone(), v.clone_box()); + current.insert(k.clone(), v.clone()); if current.len() as i64 >= Self::LOAD_BATCH_SIZE { chunks.push(std::mem::take(&mut current)); } @@ -525,11 +525,11 @@ impl PoolBuilder { // Iterate by index because we mutate package_batches inside the loop. for batch_index in 0..package_batches.len() { - let package_batch: IndexMap<String, Option<Box<dyn ConstraintInterface>>> = - package_batches[batch_index] - .iter() - .map(|(k, v)| (k.clone(), Some(v.clone_box()))) - .collect(); + let package_batch: IndexMap<String, Option<AnyConstraint>> = package_batches + [batch_index] + .iter() + .map(|(k, v)| (k.clone(), Some(v.clone()))) + .collect(); let result = repository.load_packages( package_batch, self.acceptable_stabilities.clone(), @@ -598,18 +598,18 @@ impl PoolBuilder { } // PHP: array_chunk(array_merge(...$packageBatches), self::LOAD_BATCH_SIZE, true) - let mut merged: IndexMap<String, Box<dyn ConstraintInterface>> = IndexMap::new(); + let mut merged: IndexMap<String, AnyConstraint> = IndexMap::new(); for batch in &package_batches { for (k, v) in batch { - merged.insert(k.clone(), v.clone_box()); + merged.insert(k.clone(), v.clone()); } } // Rebuild chunks from merged. package_batches = { - let mut chunks: Vec<IndexMap<String, Box<dyn ConstraintInterface>>> = Vec::new(); - let mut current: IndexMap<String, Box<dyn ConstraintInterface>> = IndexMap::new(); + let mut chunks: Vec<IndexMap<String, AnyConstraint>> = Vec::new(); + let mut current: IndexMap<String, AnyConstraint> = IndexMap::new(); for (k, v) in merged.iter() { - current.insert(k.clone(), v.clone_box()); + current.insert(k.clone(), v.clone()); if current.len() as i64 >= Self::LOAD_BATCH_SIZE { chunks.push(std::mem::take(&mut current)); } @@ -929,7 +929,7 @@ impl PoolBuilder { self.mark_package_name_for_loading( request, &replacer_name, - &MatchAllConstraint::new(), + &MatchAllConstraint::new(None).into(), ); } else { let pkgs: Vec<Box<dyn BasePackage>> = @@ -1050,8 +1050,8 @@ impl PoolBuilder { fn mark_package_name_for_loading_if_required(&mut self, request: &Request, name: &str) { if self.is_root_require(request, name) { - let cons = request.get_requires()[name].clone_box(); - self.mark_package_name_for_loading(request, name, &*cons); + let cons = request.get_requires()[name].clone(); + self.mark_package_name_for_loading(request, name, &cons); } let pkgs: Vec<Box<dyn BasePackage>> = diff --git a/crates/shirabe/src/dependency_resolver/pool_optimizer.rs b/crates/shirabe/src/dependency_resolver/pool_optimizer.rs index 326587a..27b3ffb 100644 --- a/crates/shirabe/src/dependency_resolver/pool_optimizer.rs +++ b/crates/shirabe/src/dependency_resolver/pool_optimizer.rs @@ -6,9 +6,9 @@ use anyhow::Result; use indexmap::IndexMap; use shirabe_php_shim::{LogicException, PhpMixed, implode, ksort, spl_object_hash}; use shirabe_semver::compiling_matcher::CompilingMatcher; -use shirabe_semver::constraint::Constraint; -use shirabe_semver::constraint::ConstraintInterface; +use shirabe_semver::constraint::AnyConstraint; use shirabe_semver::constraint::MultiConstraint; +use shirabe_semver::constraint::SimpleConstraint; use shirabe_semver::intervals::Intervals; use crate::dependency_resolver::PolicyInterface; @@ -29,12 +29,10 @@ pub struct PoolOptimizer { irremovable_packages: IndexMap<i64, bool>, /// @var array<string, array<string, ConstraintInterface>> - require_constraints_per_package: - IndexMap<String, IndexMap<String, Box<dyn ConstraintInterface>>>, + require_constraints_per_package: IndexMap<String, IndexMap<String, AnyConstraint>>, /// @var array<string, array<string, ConstraintInterface>> - conflict_constraints_per_package: - IndexMap<String, IndexMap<String, Box<dyn ConstraintInterface>>>, + conflict_constraints_per_package: IndexMap<String, IndexMap<String, AnyConstraint>>, /// @var array<int, true> packages_to_remove: IndexMap<i64, bool>, @@ -90,23 +88,27 @@ impl PoolOptimizer { } fn prepare(&mut self, request: &Request, pool: &Pool) { - let mut irremovable_package_constraint_groups: IndexMap< - String, - Vec<Box<dyn ConstraintInterface>>, - > = IndexMap::new(); + let mut irremovable_package_constraint_groups: IndexMap<String, Vec<AnyConstraint>> = + IndexMap::new(); // Mark fixed or locked packages as irremovable for (_, package) in request.get_fixed_or_locked_packages() { irremovable_package_constraint_groups .entry(PackageInterface::get_name(package.as_ref()).to_string()) .or_insert_with(Vec::new) - .push(Box::new(Constraint::new("==", package.get_version()))); + .push( + SimpleConstraint::new( + "==".to_string(), + package.get_version().to_string(), + None, + ) + .into(), + ); } // Extract requested package requirements for (require, constraint) in request.get_requires() { - // TODO(phase-b): clone Box<dyn ConstraintInterface> - self.extract_require_constraints_per_package(require, todo!("constraint.clone_box()")); + self.extract_require_constraints_per_package(require, constraint.clone()); } // First pass over all packages to extract information and mark package constraints irremovable @@ -115,16 +117,14 @@ impl PoolOptimizer { for link in package.get_requires().values() { self.extract_require_constraints_per_package( link.get_target(), - // TODO(phase-b): clone constraint - todo!("link.get_constraint().clone_box()"), + link.get_constraint().clone(), ); } // Extract package conflicts for link in package.get_conflicts().values() { self.extract_conflict_constraints_per_package( link.get_target(), - // TODO(phase-b): clone constraint - todo!("link.get_constraint().clone_box()"), + link.get_constraint().clone(), ); } @@ -138,16 +138,14 @@ impl PoolOptimizer { } } - let mut irremovable_package_constraints: IndexMap<String, Box<dyn ConstraintInterface>> = - IndexMap::new(); + let mut irremovable_package_constraints: IndexMap<String, AnyConstraint> = IndexMap::new(); for (package_name, constraints) in irremovable_package_constraint_groups { - // TODO(phase-b): MultiConstraint::new signature; move ownership of constraints vec irremovable_package_constraints.insert( package_name, if 1 == constraints.len() { - todo!("constraints[0] moved out") + constraints.into_iter().next().unwrap() } else { - Box::new(MultiConstraint::new(constraints, false)) + MultiConstraint::new(constraints, false, None).into() }, ); } @@ -165,8 +163,8 @@ impl PoolOptimizer { .get(PackageInterface::get_name(package.as_ref())) .unwrap(); if CompilingMatcher::r#match( - constraint.as_ref(), - Constraint::OP_EQ, + constraint, + SimpleConstraint::OP_EQ, package.get_version().to_string(), ) { self.mark_package_irremovable(package.as_ref()); @@ -262,8 +260,8 @@ impl PoolOptimizer { let mut group_hash_parts: Vec<String> = vec![]; if CompilingMatcher::r#match( - require_constraint.as_ref(), - Constraint::OP_EQ, + require_constraint, + SimpleConstraint::OP_EQ, package.get_version().to_string(), ) { group_hash_parts.push(format!( @@ -276,7 +274,7 @@ impl PoolOptimizer { for (_, link) in package.get_replaces() { if CompilingMatcher::r#match( link.get_constraint(), - Constraint::OP_EQ, + SimpleConstraint::OP_EQ, package.get_version().to_string(), ) { // Use the same hash part as the regular require hash because that's what the replacement does @@ -293,8 +291,8 @@ impl PoolOptimizer { { for (_, conflict_constraint) in conflict_constraints { if CompilingMatcher::r#match( - conflict_constraint.as_ref(), - Constraint::OP_EQ, + conflict_constraint, + SimpleConstraint::OP_EQ, package.get_version().to_string(), ) { group_hash_parts.push(format!( @@ -415,7 +413,7 @@ impl PoolOptimizer { // performance more than the additional few packages that could be filtered out would benefit the process. subhash.insert( link.get_target().to_string(), - link.get_constraint().__to_string(), + link.get_constraint().to_string(), ); } @@ -638,7 +636,7 @@ impl PoolOptimizer { if false == CompilingMatcher::r#match( link_constraint, - Constraint::OP_EQ, + SimpleConstraint::OP_EQ, version_str, ) { @@ -662,13 +660,13 @@ impl PoolOptimizer { fn extract_require_constraints_per_package( &mut self, package: &str, - constraint: Box<dyn ConstraintInterface>, + constraint: AnyConstraint, ) { for expanded in self.expand_disjunctive_multi_constraints(constraint) { self.require_constraints_per_package .entry(package.to_string()) .or_insert_with(IndexMap::new) - .insert(expanded.__to_string(), expanded); + .insert(expanded.to_string(), expanded); } } @@ -680,32 +678,28 @@ impl PoolOptimizer { fn extract_conflict_constraints_per_package( &mut self, package: &str, - constraint: Box<dyn ConstraintInterface>, + constraint: AnyConstraint, ) { for expanded in self.expand_disjunctive_multi_constraints(constraint) { self.conflict_constraints_per_package .entry(package.to_string()) .or_insert_with(IndexMap::new) - .insert(expanded.__to_string(), expanded); + .insert(expanded.to_string(), expanded); } } /// @return ConstraintInterface[] fn expand_disjunctive_multi_constraints( &self, - constraint: Box<dyn ConstraintInterface>, - ) -> Vec<Box<dyn ConstraintInterface>> { - let constraint = Intervals::compact_constraint(&*constraint).unwrap_or(constraint); + constraint: AnyConstraint, + ) -> Vec<AnyConstraint> { + let constraint = Intervals::compact_constraint(&constraint).unwrap_or(constraint); - if let Some(multi) = constraint.as_any().downcast_ref::<MultiConstraint>() { - if multi.is_disjunctive() { + if let Some(multi) = constraint.as_multi_constraint() { + if multi.is_disjunctive_mc() { // No need to call ourselves recursively here because Intervals::compactConstraint() ensures that there // are no nested disjunctive MultiConstraint instances possible - return multi - .get_constraints() - .iter() - .map(|c| c.clone_box()) - .collect(); + return multi.get_constraints().iter().map(|c| c.clone()).collect(); } } diff --git a/crates/shirabe/src/dependency_resolver/problem.rs b/crates/shirabe/src/dependency_resolver/problem.rs index a69a4a2..4436ec8 100644 --- a/crates/shirabe/src/dependency_resolver/problem.rs +++ b/crates/shirabe/src/dependency_resolver/problem.rs @@ -9,9 +9,9 @@ use shirabe_php_shim::{ phpversion, spl_object_hash, sprintf, str_replace, str_starts_with, stripos, strpos, strtolower, substr, substr_count, version_compare, }; -use shirabe_semver::constraint::Constraint; -use shirabe_semver::constraint::ConstraintInterface; +use shirabe_semver::constraint::AnyConstraint; use shirabe_semver::constraint::MultiConstraint; +use shirabe_semver::constraint::SimpleConstraint; use crate::advisory::SecurityAdvisory; use crate::dependency_resolver::Pool; @@ -92,14 +92,13 @@ impl Problem { let reason_data = rule_ref.get_reason_data(); // TODO(phase-b): reason_data for RULE_ROOT_REQUIRE; extract via ReasonData::RootRequire variant. - let (package_name, constraint): (String, Option<&dyn ConstraintInterface>) = - match reason_data { - rule::ReasonData::RootRequire { - package_name, - constraint, - } => (package_name.clone(), Some(constraint.as_ref())), - _ => (String::new(), None), - }; + let (package_name, constraint): (String, Option<&AnyConstraint>) = match reason_data { + rule::ReasonData::RootRequire { + package_name, + constraint, + } => (package_name.clone(), Some(constraint)), + _ => (String::new(), None), + }; let packages = pool.compute_what_provides(&package_name, constraint); if packages.len() == 0 { @@ -393,7 +392,7 @@ impl Problem { pool: &mut Pool, is_verbose: bool, package_name: &str, - constraint: Option<&dyn ConstraintInterface>, + constraint: Option<&AnyConstraint>, ) -> (String, String) { if PlatformRepository::is_platform_package(package_name) { // handle php/php-*/hhvm @@ -572,7 +571,7 @@ impl Problem { if let Some(c) = constraint { if c.is_constraint() - && c.get_operator() == Constraint::STR_OP_EQ + && c.get_operator() == SimpleConstraint::STR_OP_EQ && Preg::is_match3(r"{^dev-.*#.*}", &c.get_pretty_string(), None).unwrap_or(false) { let new_constraint = @@ -580,17 +579,25 @@ impl Problem { .unwrap_or_else(|_| c.get_pretty_string()); let packages = repository_set.find_packages( package_name, - Some(Box::new(MultiConstraint::new( - vec![ - Box::new(Constraint::new(Constraint::STR_OP_EQ, &new_constraint)) - as Box<dyn ConstraintInterface>, - Box::new(Constraint::new( - Constraint::STR_OP_EQ, - &str_replace("#", "+", &new_constraint), - )) as Box<dyn ConstraintInterface>, - ], - false, - ))), + Some( + MultiConstraint::new( + vec![ + AnyConstraint::Simple(SimpleConstraint::new( + SimpleConstraint::STR_OP_EQ.to_string(), + new_constraint.clone(), + None, + )), + AnyConstraint::Simple(SimpleConstraint::new( + SimpleConstraint::STR_OP_EQ.to_string(), + str_replace("#", "+", &new_constraint), + None, + )), + ], + false, + None, + ) + .into(), + ), 0, ); if packages.len() > 0 { @@ -618,15 +625,21 @@ impl Problem { // first check if the actual requested package is found in normal conditions // if so it must mean it is rejected by another constraint than the one given here - let packages = - repository_set.find_packages(package_name, constraint.map(|c| c.clone_box()), 0); + let packages = repository_set.find_packages(package_name, constraint.map(|c| c.clone()), 0); if packages.len() > 0 { let root_reqs = repository_set.get_root_requires(); if root_reqs.contains_key(package_name) { let filtered: Vec<&Box<dyn BasePackage>> = packages .iter() .filter(|p| { - root_reqs[package_name].matches(&Constraint::new("==", p.get_version())) + root_reqs[package_name].matches( + &SimpleConstraint::new( + "==".to_string(), + p.get_version().to_string(), + None, + ) + .into(), + ) }) .collect(); if filtered.len() == 0 { @@ -663,7 +676,14 @@ impl Problem { let filtered: Vec<&Box<dyn BasePackage>> = packages .iter() .filter(|p| { - temp_reqs[&name].matches(&Constraint::new("==", p.get_version())) + temp_reqs[&name].matches( + &SimpleConstraint::new( + "==".to_string(), + p.get_version().to_string(), + None, + ) + .into(), + ) }) .collect(); if filtered.len() == 0 { @@ -696,10 +716,23 @@ impl Problem { } if let Some(ref lp) = locked_package { - let fixed_constraint = Constraint::new("==", lp.get_version()); + let fixed_constraint = AnyConstraint::from(SimpleConstraint::new( + "==".to_string(), + lp.get_version().to_string(), + None, + )); let filtered: Vec<&Box<dyn BasePackage>> = packages .iter() - .filter(|p| fixed_constraint.matches(&Constraint::new("==", p.get_version()))) + .filter(|p| { + fixed_constraint.matches( + &SimpleConstraint::new( + "==".to_string(), + p.get_version().to_string(), + None, + ) + .into(), + ) + }) .collect(); if filtered.len() == 0 { return ( @@ -837,14 +870,14 @@ impl Problem { // check if the package is found when bypassing stability checks let packages = repository_set.find_packages( package_name, - constraint.map(|c| c.clone_box()), + constraint.map(|c| c.clone()), RepositorySet::ALLOW_UNACCEPTABLE_STABILITIES, ); if packages.len() > 0 { // we must first verify if a valid package would be found in a lower priority repository let all_repos_packages = repository_set.find_packages( package_name, - constraint.map(|c| c.clone_box()), + constraint.map(|c| c.clone()), RepositorySet::ALLOW_SHADOWED_REPOSITORIES, ); if all_repos_packages.len() > 0 { @@ -887,7 +920,7 @@ impl Problem { // we must first verify if a valid package would be found in a lower priority repository let all_repos_packages = repository_set.find_packages( package_name, - constraint.map(|c| c.clone_box()), + constraint.map(|c| c.clone()), RepositorySet::ALLOW_SHADOWED_REPOSITORIES, ); if all_repos_packages.len() > 0 { @@ -993,7 +1026,7 @@ impl Problem { packages: &Vec<Box<dyn BasePackage>>, is_verbose: bool, pool: Option<&Pool>, - constraint: Option<&dyn ConstraintInterface>, + constraint: Option<&AnyConstraint>, use_removed_version_group: bool, ) -> String { struct PreparedEntry { @@ -1213,7 +1246,7 @@ impl Problem { higher_repo_packages: &Vec<Box<dyn BasePackage>>, all_repos_packages: &Vec<Box<dyn BasePackage>>, reason: &str, - constraint: Option<&dyn ConstraintInterface>, + constraint: Option<&AnyConstraint>, ) -> (String, String) { let mut next_repo_packages: Vec<Box<dyn BasePackage>> = Vec::new(); let mut next_repo: Option<Box<dyn crate::repository::RepositoryInterface>> = None; @@ -1346,10 +1379,10 @@ impl Problem { } /// Turns a constraint into text usable in a sentence describing a request - pub(crate) fn constraint_to_text(constraint: Option<&dyn ConstraintInterface>) -> String { + pub(crate) fn constraint_to_text(constraint: Option<&AnyConstraint>) -> String { if let Some(c) = constraint { if c.is_constraint() - && c.get_operator() == Constraint::STR_OP_EQ + && c.get_operator() == SimpleConstraint::STR_OP_EQ && !str_starts_with(&c.get_version(), "dev-") { if !Preg::is_match3(r"{^\d+(?:\.\d+)*$}", &c.get_pretty_string(), None) diff --git a/crates/shirabe/src/dependency_resolver/request.rs b/crates/shirabe/src/dependency_resolver/request.rs index d181bd2..e2bfc3c 100644 --- a/crates/shirabe/src/dependency_resolver/request.rs +++ b/crates/shirabe/src/dependency_resolver/request.rs @@ -2,7 +2,7 @@ use indexmap::IndexMap; use shirabe_php_shim::{LogicException, spl_object_hash, strtolower}; -use shirabe_semver::constraint::ConstraintInterface; +use shirabe_semver::constraint::AnyConstraint; use shirabe_semver::constraint::MatchAllConstraint; use crate::package::BasePackage; @@ -43,7 +43,7 @@ pub enum UpdateAllowTransitiveDeps { #[derive(Debug)] pub struct Request { pub(crate) locked_repository: Option<LockArrayRepository>, - pub(crate) requires: IndexMap<String, Box<dyn ConstraintInterface>>, + pub(crate) requires: IndexMap<String, AnyConstraint>, pub(crate) fixed_packages: IndexMap<String, Box<dyn BasePackage>>, pub(crate) locked_packages: IndexMap<String, Box<dyn BasePackage>>, pub(crate) fixed_locked_packages: IndexMap<String, Box<dyn BasePackage>>, @@ -69,10 +69,10 @@ impl Request { pub fn require_name( &mut self, package_name: &str, - constraint: Option<Box<dyn ConstraintInterface>>, + constraint: Option<AnyConstraint>, ) -> anyhow::Result<()> { let package_name = strtolower(package_name); - let constraint = constraint.unwrap_or_else(|| Box::new(MatchAllConstraint::new())); + let constraint = constraint.unwrap_or_else(|| MatchAllConstraint::new(None).into()); if self.requires.contains_key(&package_name) { return Err(LogicException { message: format!( @@ -155,7 +155,7 @@ impl Request { == UpdateAllowTransitiveDeps::UpdateListedWithTransitiveDeps } - pub fn get_requires(&self) -> &IndexMap<String, Box<dyn ConstraintInterface>> { + pub fn get_requires(&self) -> &IndexMap<String, AnyConstraint> { &self.requires } diff --git a/crates/shirabe/src/dependency_resolver/rule.rs b/crates/shirabe/src/dependency_resolver/rule.rs index 90223e2..fbc8521 100644 --- a/crates/shirabe/src/dependency_resolver/rule.rs +++ b/crates/shirabe/src/dependency_resolver/rule.rs @@ -10,8 +10,8 @@ use shirabe_php_shim::{ LogicException, PhpMixed, RuntimeException, abs, array_filter, array_keys, array_shift, array_values, implode, is_object, }; -use shirabe_semver::constraint::Constraint; -use shirabe_semver::constraint::ConstraintInterface; +use shirabe_semver::constraint::AnyConstraint; +use shirabe_semver::constraint::SimpleConstraint; use crate::dependency_resolver::GenericRule; use crate::dependency_resolver::MultiConflictRule; @@ -36,7 +36,7 @@ pub enum ReasonData { Int(i64), RootRequire { package_name: String, - constraint: Box<dyn ConstraintInterface>, + constraint: AnyConstraint, }, Fixed { package: Box<dyn BasePackage>, @@ -238,10 +238,14 @@ impl Rule { if pool.is_unacceptable_fixed_or_locked_package(p) { return true; } - if !link - .get_constraint() - .matches(&Constraint::new("=", p.get_version())) - { + if !link.get_constraint().matches( + &SimpleConstraint::new( + "=".to_string(), + p.get_version().to_string(), + None, + ) + .into(), + ) { return true; } // required package was locked but has been unlocked and still matches @@ -275,7 +279,14 @@ impl Rule { if pool.is_unacceptable_fixed_or_locked_package(p) { return true; } - if !constraint.matches(&Constraint::new("=", p.get_version())) { + if !constraint.matches( + &SimpleConstraint::new( + "=".to_string(), + p.get_version().to_string(), + None, + ) + .into(), + ) { return true; } break; @@ -345,12 +356,11 @@ impl Rule { match self.get_reason() { r if r == RULE_ROOT_REQUIRE => { let reason_data = self.get_reason_data(); - let (package_name, constraint): (&str, &dyn ConstraintInterface) = match reason_data - { + let (package_name, constraint): (&str, &AnyConstraint) = match reason_data { ReasonData::RootRequire { package_name, constraint, - } => (package_name.as_str(), constraint.as_ref()), + } => (package_name.as_str(), constraint), _ => return String::new(), }; @@ -712,7 +722,7 @@ impl Rule { pool: &Pool, packages: Vec<Box<dyn BasePackage>>, is_verbose: bool, - constraint: Option<&dyn ConstraintInterface>, + constraint: Option<&AnyConstraint>, use_removed_version_group: bool, ) -> String { Problem::get_package_list( @@ -730,7 +740,7 @@ impl Rule { pool: &Pool, literals: &[i64], is_verbose: bool, - constraint: Option<&dyn ConstraintInterface>, + constraint: Option<&AnyConstraint>, use_removed_version_group: bool, ) -> String { let mut packages: Vec<Box<dyn BasePackage>> = vec![]; diff --git a/crates/shirabe/src/dependency_resolver/rule_set_generator.rs b/crates/shirabe/src/dependency_resolver/rule_set_generator.rs index 45f1331..076ce42 100644 --- a/crates/shirabe/src/dependency_resolver/rule_set_generator.rs +++ b/crates/shirabe/src/dependency_resolver/rule_set_generator.rs @@ -205,14 +205,14 @@ impl RuleSetGenerator { } for link in package.get_requires().values() { - let mut constraint = link.get_constraint().clone_box(); + let mut constraint = link.get_constraint().clone(); if platform_requirement_filter.is_ignored(link.get_target()) { continue; } else if let Some(ignore_list_filter) = platform_requirement_filter .as_any() .downcast_ref::<IgnoreListPlatformRequirementFilter>( ) { - let fallback = constraint.clone_box(); + let fallback = constraint.clone(); constraint = ignore_list_filter .filter_constraint(link.get_target(), constraint, true) .unwrap_or(fallback); @@ -221,7 +221,7 @@ impl RuleSetGenerator { let possible_requires: Vec<Box<dyn PackageInterface>> = self .pool .borrow_mut() - .what_provides(link.get_target(), Some(&*constraint)) + .what_provides(link.get_target(), Some(&constraint)) .into_iter() .map(|p| p.clone_package_box()) .collect(); @@ -258,14 +258,14 @@ impl RuleSetGenerator { continue; } - let mut constraint = link.get_constraint().clone_box(); + let mut constraint = link.get_constraint().clone(); if platform_requirement_filter.is_ignored(link.get_target()) { continue; } else if let Some(ignore_list_filter) = platform_requirement_filter .as_any() .downcast_ref::<IgnoreListPlatformRequirementFilter>( ) { - let fallback = constraint.clone_box(); + let fallback = constraint.clone(); constraint = ignore_list_filter .filter_constraint(link.get_target(), constraint, false) .unwrap_or(fallback); @@ -274,7 +274,7 @@ impl RuleSetGenerator { let conflicts = self .pool .borrow_mut() - .what_provides(link.get_target(), Some(&*constraint)); + .what_provides(link.get_target(), Some(&constraint)); for conflict in &conflicts { // define the conflict rule for regular packages, for alias packages it's only needed if the name @@ -354,14 +354,14 @@ impl RuleSetGenerator { } for (package_name, constraint) in request.get_requires() { - let mut constraint = constraint.clone_box(); + let mut constraint = constraint.clone(); if platform_requirement_filter.is_ignored(package_name) { continue; } else if let Some(ignore_list_filter) = platform_requirement_filter .as_any() .downcast_ref::<IgnoreListPlatformRequirementFilter>( ) { - let fallback = constraint.clone_box(); + let fallback = constraint.clone(); constraint = ignore_list_filter .filter_constraint(package_name, constraint, true) .unwrap_or(fallback); @@ -370,7 +370,7 @@ impl RuleSetGenerator { let packages: Vec<Box<dyn PackageInterface>> = self .pool .borrow_mut() - .what_provides(package_name, Some(&*constraint)) + .what_provides(package_name, Some(&constraint)) .into_iter() .map(|p| p.clone_package_box()) .collect(); diff --git a/crates/shirabe/src/dependency_resolver/security_advisory_pool_filter.rs b/crates/shirabe/src/dependency_resolver/security_advisory_pool_filter.rs index 742a709..4daf641 100644 --- a/crates/shirabe/src/dependency_resolver/security_advisory_pool_filter.rs +++ b/crates/shirabe/src/dependency_resolver/security_advisory_pool_filter.rs @@ -8,7 +8,8 @@ use crate::dependency_resolver::Request; use crate::package::PackageInterface; use crate::repository::RepositoryInterface; use indexmap::IndexMap; -use shirabe_semver::constraint::Constraint; +use shirabe_semver::constraint::AnyConstraint; +use shirabe_semver::constraint::SimpleConstraint; #[derive(Debug)] pub struct SecurityAdvisoryPoolFilter { @@ -64,7 +65,9 @@ impl SecurityAdvisoryPoolFilter { continue; } - let package_constraint = Constraint::new("==", package.get_version()); + let package_constraint = + SimpleConstraint::new("==".to_string(), package.get_version().to_string(), None) + .into(); for advisory in &advisory_map[&package_name] { // advisory is PartialSecurityAdvisory or SecurityAdvisory; both have affected_versions: Box<dyn ConstraintInterface> if advisory.affected_versions.matches(&package_constraint) { diff --git a/crates/shirabe/src/dependency_resolver/solver.rs b/crates/shirabe/src/dependency_resolver/solver.rs index e4539d2..820f792 100644 --- a/crates/shirabe/src/dependency_resolver/solver.rs +++ b/crates/shirabe/src/dependency_resolver/solver.rs @@ -8,7 +8,7 @@ use indexmap::IndexMap; use shirabe_php_shim::{ PhpMixed, array_pop, array_shift, array_unshift, microtime, spl_object_hash, sprintf, }; -use shirabe_semver::constraint::ConstraintInterface; +use shirabe_semver::constraint::AnyConstraint; use crate::dependency_resolver::Decisions; use crate::dependency_resolver::GenericRule; @@ -189,8 +189,8 @@ impl Solver { // TODO(phase-b): ConstraintInterface is a PHP class — Box<dyn ConstraintInterface> // cannot be cloned. We borrow the original constraint and only allocate a fresh // box when the ignore filter rewrites it. - let mut filtered: Option<Box<dyn ConstraintInterface>> = None; - let constraint_ref: &dyn ConstraintInterface = constraint.as_ref(); + let mut filtered: Option<AnyConstraint> = None; + let constraint_ref: &AnyConstraint = constraint; if platform_requirement_filter.is_ignored(package_name) { continue; } else if let Some(ignore_filter) = platform_requirement_filter @@ -204,8 +204,7 @@ impl Solver { let _ = &mut filtered; } - let active_constraint: &dyn ConstraintInterface = - filtered.as_deref().unwrap_or(constraint_ref); + let active_constraint: &AnyConstraint = filtered.as_ref().unwrap_or(constraint_ref); if self .pool diff --git a/crates/shirabe/src/filter/platform_requirement_filter/ignore_list_platform_requirement_filter.rs b/crates/shirabe/src/filter/platform_requirement_filter/ignore_list_platform_requirement_filter.rs index f6b8091..d69773c 100644 --- a/crates/shirabe/src/filter/platform_requirement_filter/ignore_list_platform_requirement_filter.rs +++ b/crates/shirabe/src/filter/platform_requirement_filter/ignore_list_platform_requirement_filter.rs @@ -1,10 +1,10 @@ //! ref: composer/src/Composer/Filter/PlatformRequirementFilter/IgnoreListPlatformRequirementFilter.php use shirabe_external_packages::composer::pcre::Preg; -use shirabe_semver::constraint::Constraint; -use shirabe_semver::constraint::ConstraintInterface; +use shirabe_semver::constraint::AnyConstraint; use shirabe_semver::constraint::MatchAllConstraint; use shirabe_semver::constraint::MultiConstraint; +use shirabe_semver::constraint::SimpleConstraint; use shirabe_semver::interval::Interval; use shirabe_semver::intervals::Intervals; @@ -41,9 +41,9 @@ impl IgnoreListPlatformRequirementFilter { pub fn filter_constraint( &self, req: &str, - constraint: Box<dyn ConstraintInterface>, + constraint: AnyConstraint, allow_upper_bound_override: bool, - ) -> anyhow::Result<Box<dyn ConstraintInterface>> { + ) -> anyhow::Result<AnyConstraint> { if !PlatformRepository::is_platform_package(req) { return Ok(constraint); } @@ -53,20 +53,26 @@ impl IgnoreListPlatformRequirementFilter { } if Preg::is_match(&self.ignore_regex, req)? { - return Ok(Box::new(MatchAllConstraint::new())); + return Ok(MatchAllConstraint::new(None).into()); } - let intervals = Intervals::get(&*constraint)?; + let intervals = Intervals::get(&constraint)?; let last = intervals.numeric.last(); if let Some(last) = last { if last.get_end().to_string() != Interval::until_positive_infinity().to_string() { - let constraint = Box::new(MultiConstraint::new( + let constraint = MultiConstraint::new( vec![ constraint, - Box::new(Constraint::new(">=", last.get_end().get_version())), + AnyConstraint::Simple(SimpleConstraint::new( + ">=".to_string(), + last.get_end().get_version().to_string(), + None, + )), ], false, - )); + None, + ) + .into(); return Ok(constraint); } } diff --git a/crates/shirabe/src/installed_versions.rs b/crates/shirabe/src/installed_versions.rs index 644cc8e..d52deac 100644 --- a/crates/shirabe/src/installed_versions.rs +++ b/crates/shirabe/src/installed_versions.rs @@ -171,7 +171,7 @@ impl InstalledVersions { let constraint = parser.parse_constraints(constraint.unwrap_or(""))?; let provided = parser.parse_constraints(&Self::get_version_ranges(package_name)?)?; - Ok(provided.matches(&*constraint)) + Ok(provided.matches(&constraint)) } /// Returns a version constraint representing all the range(s) which are installed for a given package diff --git a/crates/shirabe/src/installer.rs b/crates/shirabe/src/installer.rs index 3ccb65f..8b68a78 100644 --- a/crates/shirabe/src/installer.rs +++ b/crates/shirabe/src/installer.rs @@ -97,8 +97,8 @@ use crate::repository::RepositorySet; use crate::repository::RootPackageRepository; use crate::script::ScriptEvents; use crate::util::Platform; -use shirabe_semver::constraint::Constraint; -use shirabe_semver::constraint::ConstraintInterface; +use shirabe_semver::constraint::AnyConstraint; +use shirabe_semver::constraint::SimpleConstraint; #[derive(Debug)] pub struct Installer { @@ -144,7 +144,7 @@ pub struct Installer { pub(crate) suggested_packages_reporter: SuggestedPackagesReporter, pub(crate) platform_requirement_filter: Box<dyn PlatformRequirementFilterInterface>, pub(crate) additional_fixed_repository: Option<Box<dyn RepositoryInterface>>, - pub(crate) temporary_constraints: IndexMap<String, Box<dyn ConstraintInterface>>, + pub(crate) temporary_constraints: IndexMap<String, AnyConstraint>, } impl Installer { @@ -1049,8 +1049,7 @@ impl Installer { } for (_key, link) in &root_requires { if PlatformRepository::is_platform_package(link.get_target()) { - request - .require_name(link.get_target(), Some(link.get_constraint().clone_box()))?; + request.require_name(link.get_target(), Some(link.get_constraint().clone()))?; } } @@ -1060,8 +1059,7 @@ impl Installer { .get_platform_requirements(self.dev_mode)? { if !root_requires.contains_key(link.get_target()) { - request - .require_name(link.get_target(), Some(link.get_constraint().clone_box()))?; + request.require_name(link.get_target(), Some(link.get_constraint().clone()))?; } } drop(root_requires); @@ -1249,7 +1247,7 @@ impl Installer { let minimum_stability: String; let mut stability_flags: IndexMap<String, i64>; - let requires: IndexMap<String, Box<dyn ConstraintInterface>>; + let requires: IndexMap<String, AnyConstraint>; if for_update { minimum_stability = self.package.get_minimum_stability().to_string(); stability_flags = self.package.get_stability_flags().clone(); @@ -1263,9 +1261,9 @@ impl Installer { req_links.insert(k, v); } // Translate to constraint map for downstream uniform handling. - let mut tmp: IndexMap<String, Box<dyn ConstraintInterface>> = IndexMap::new(); + let mut tmp: IndexMap<String, AnyConstraint> = IndexMap::new(); for (k, link) in req_links { - tmp.insert(k, link.get_constraint().clone_box()); + tmp.insert(k, link.get_constraint().clone()); } requires = tmp; } else { @@ -1286,16 +1284,19 @@ impl Installer { }) .unwrap_or_default(); - let mut tmp: IndexMap<String, Box<dyn ConstraintInterface>> = IndexMap::new(); + let mut tmp: IndexMap<String, AnyConstraint> = IndexMap::new(); for package in locked_repository.unwrap().get_packages() { - let mut constraint = Constraint::new("=", package.get_version().to_string()); - constraint.set_pretty_string(Some(package.get_pretty_version().to_string())); - tmp.insert(package.get_name().to_string(), Box::new(constraint)); + let constraint = SimpleConstraint::new( + "=".to_string(), + package.get_version().to_string(), + Some(package.get_pretty_version().to_string()), + ); + tmp.insert(package.get_name().to_string(), constraint.into()); } requires = tmp; } - let mut root_requires: IndexMap<String, Box<dyn ConstraintInterface>> = IndexMap::new(); + let mut root_requires: IndexMap<String, AnyConstraint> = IndexMap::new(); for (req, mut constraint) in requires { // skip platform requirements from the root package to avoid filtering out existing platform packages if self.platform_requirement_filter.is_ignored(&req) { @@ -1307,7 +1308,10 @@ impl Installer { { constraint = filter .filter_constraint(&req, constraint, false) - .unwrap_or_else(|_| Box::new(Constraint::new("=", String::new()))); + .unwrap_or_else(|_| { + SimpleConstraint::new("=".to_string(), String::new().to_string(), None) + .into() + }); } root_requires.insert(req, constraint); } @@ -1327,8 +1331,7 @@ impl Installer { // TODO(phase-b): convert root_aliases (Vec<IndexMap<String, String>>) into Vec<RootAliasInput> let root_aliases_input: Vec<crate::repository::RootAliasInput> = vec![]; let _ = root_aliases; - // TODO(phase-b): temporary_constraints holds Box<dyn ConstraintInterface> which can't Clone - let temporary_constraints: IndexMap<String, Box<dyn ConstraintInterface>> = IndexMap::new(); + let temporary_constraints: IndexMap<String, AnyConstraint> = IndexMap::new(); let mut repository_set = RepositorySet::new( &minimum_stability, stability_flags, @@ -1467,7 +1470,14 @@ impl Installer { .get(package.get_name()) .unwrap() .get_constraint() - .matches(&Constraint::new("=", package.get_version().to_string())) + .matches( + &SimpleConstraint::new( + "=".to_string(), + package.get_version().to_string(), + None, + ) + .into(), + ) { // TODO(phase-b): fix_package needs owned Box<dyn BasePackage> let _ = &package; @@ -1510,10 +1520,14 @@ impl Installer { { request.require_name( locked_package.get_name(), - Some(Box::new(Constraint::new( - "==", - locked_package.get_version().to_string(), - ))), + Some( + SimpleConstraint::new( + "==".to_string(), + locked_package.get_version().to_string(), + None, + ) + .into(), + ), )?; } } @@ -1525,7 +1539,7 @@ impl Installer { } } for (_key, link) in &links { - request.require_name(link.get_target(), Some(link.get_constraint().clone_box()))?; + request.require_name(link.get_target(), Some(link.get_constraint().clone()))?; } } Ok(()) @@ -1674,7 +1688,7 @@ impl Installer { pub fn set_temporary_constraints( &mut self, - constraints: IndexMap<String, Box<dyn ConstraintInterface>>, + constraints: IndexMap<String, AnyConstraint>, ) -> &mut Self { self.temporary_constraints = constraints; diff --git a/crates/shirabe/src/package/alias_package.rs b/crates/shirabe/src/package/alias_package.rs index 0d687a1..e9b5dbf 100644 --- a/crates/shirabe/src/package/alias_package.rs +++ b/crates/shirabe/src/package/alias_package.rs @@ -3,7 +3,8 @@ use chrono::{DateTime, Utc}; use indexmap::IndexMap; use shirabe_php_shim::{PhpMixed, in_array}; -use shirabe_semver::constraint::Constraint; +use shirabe_semver::constraint::AnyConstraint; +use shirabe_semver::constraint::SimpleConstraint; use crate::package::BasePackage; use crate::package::Link; @@ -177,18 +178,18 @@ impl AliasPackage { for link in &links { // link is self.version, but must be replacing also the replaced version if link.get_pretty_constraint().unwrap_or("") == "self.version" { - let mut constraint = Constraint::new("=", &self.version); + let constraint = SimpleConstraint::new( + "=".to_string(), + self.version.to_string(), + Some(pretty_version.clone()), + ); let new_link = Link::new( link.get_source().to_string(), link.get_target().to_string(), - Box::new(constraint.clone()), + constraint.into(), Some(link_type.to_string()), Some(pretty_version.clone()), ); - shirabe_semver::constraint::ConstraintInterface::set_pretty_string( - &mut constraint, - Some(pretty_version.clone()), - ); new_links.push(new_link); } } @@ -200,18 +201,18 @@ impl AliasPackage { if link_type == Link::TYPE_REQUIRE { self.has_self_version_requires = true; } - let mut constraint = Constraint::new("=", &self.version); + let constraint = SimpleConstraint::new( + "=".to_string(), + self.version.to_string(), + Some(pretty_version.clone()), + ); let new_link = Link::new( links[index].get_source().to_string(), links[index].get_target().to_string(), - Box::new(constraint.clone()), + constraint.into(), Some(link_type.to_string()), Some(pretty_version.clone()), ); - shirabe_semver::constraint::ConstraintInterface::set_pretty_string( - &mut constraint, - Some(pretty_version.clone()), - ); links[index] = new_link; } } diff --git a/crates/shirabe/src/package/link.rs b/crates/shirabe/src/package/link.rs index 80a98c7..d606c63 100644 --- a/crates/shirabe/src/package/link.rs +++ b/crates/shirabe/src/package/link.rs @@ -1,14 +1,14 @@ //! ref: composer/src/Composer/Package/Link.php use shirabe_php_shim::UnexpectedValueException; -use shirabe_semver::constraint::ConstraintInterface; +use shirabe_semver::constraint::AnyConstraint; use crate::package::PackageInterface; pub struct Link { pub(crate) source: String, pub(crate) target: String, - pub(crate) constraint: Box<dyn ConstraintInterface>, + pub(crate) constraint: AnyConstraint, pub(crate) description: String, pub(crate) pretty_constraint: Option<String>, } @@ -20,7 +20,7 @@ impl Clone for Link { Self { source: self.source.clone(), target: self.target.clone(), - constraint: self.constraint.clone_box(), + constraint: self.constraint.clone(), description: self.description.clone(), pretty_constraint: self.pretty_constraint.clone(), } @@ -63,7 +63,7 @@ impl Link { pub fn new( source: String, target: String, - constraint: Box<dyn ConstraintInterface>, + constraint: AnyConstraint, description: Option<String>, pretty_constraint: Option<String>, ) -> Self { @@ -94,8 +94,8 @@ impl Link { &self.target } - pub fn get_constraint(&self) -> &dyn ConstraintInterface { - &*self.constraint + pub fn get_constraint(&self) -> &AnyConstraint { + &self.constraint } pub fn get_pretty_constraint(&self) -> anyhow::Result<&str> { @@ -117,7 +117,7 @@ impl Link { self.source, self.description, self.target, - self.constraint.__to_string(), + self.constraint.to_string(), ) } diff --git a/crates/shirabe/src/package/loader/array_loader.rs b/crates/shirabe/src/package/loader/array_loader.rs index ffbe465..87d6e35 100644 --- a/crates/shirabe/src/package/loader/array_loader.rs +++ b/crates/shirabe/src/package/loader/array_loader.rs @@ -764,12 +764,10 @@ impl ArrayLoader { } }; - // TODO(phase-b): Link::new expects Box<dyn ConstraintInterface>; we have Arc<dyn ConstraintInterface + Send + Sync> - let _ = parsed_constraint; Ok(Link::new( source.to_string(), target.to_string(), - todo!("phase-b: convert Arc<dyn ConstraintInterface> to Box<dyn ConstraintInterface>"), + parsed_constraint, Some(description.to_string()), Some(pretty_constraint.to_string()), )) diff --git a/crates/shirabe/src/package/loader/validating_array_loader.rs b/crates/shirabe/src/package/loader/validating_array_loader.rs index 59fc266..a2dd5a7 100644 --- a/crates/shirabe/src/package/loader/validating_array_loader.rs +++ b/crates/shirabe/src/package/loader/validating_array_loader.rs @@ -11,9 +11,9 @@ use shirabe_php_shim::{ is_string, json_encode, parse_url_all, php_to_string, sprintf, str_replace, strcasecmp, strtolower, strtotime, substr, trigger_error, trim, var_export, }; -use shirabe_semver::constraint::Constraint; -use shirabe_semver::constraint::ConstraintInterface; +use shirabe_semver::constraint::AnyConstraint; use shirabe_semver::constraint::MatchNoneConstraint; +use shirabe_semver::constraint::SimpleConstraint; use shirabe_semver::intervals::Intervals; use crate::package::loader::InvalidPackageException; @@ -877,7 +877,8 @@ impl ValidatingArrayLoader { } } - let unbound_constraint = Constraint::new("=", "10000000-dev"); + let unbound_constraint = + SimpleConstraint::new("=".to_string(), "10000000-dev".to_string(), None).into(); let link_types: Vec<&'static str> = SUPPORTED_LINK_TYPES.keys().copied().collect(); for link_type in link_types { @@ -952,11 +953,14 @@ impl ValidatingArrayLoader { } else if (self.flags & Self::CHECK_STRICT_CONSTRAINTS) != 0 && link_type == "require" && link_constraint - .as_any() - .downcast_ref::<Constraint>() + .as_constraint() .map_or(false, |c| ["==", "="].contains(&c.get_operator())) - && Constraint::new(">=", "1.0.0.0-dev") - .matches(link_constraint.as_ref()) + && AnyConstraint::from(SimpleConstraint::new( + ">=".to_string(), + "1.0.0.0-dev".to_string(), + None, + )) + .matches(&link_constraint) { self.warnings.push(format!( "{}.{} : exact version constraints ({}) should be avoided if the package follows semantic versioning", @@ -964,8 +968,8 @@ impl ValidatingArrayLoader { )); } - let compacted = Intervals::compact_constraint(link_constraint.as_ref())?; - if compacted.as_any().is::<MatchNoneConstraint>() { + let compacted = Intervals::compact_constraint(&link_constraint)?; + if compacted.is_match_none() { self.warnings.push(format!( "{}.{} : this version constraint cannot possibly match anything ({})", link_type, package, constraint_str diff --git a/crates/shirabe/src/package/locker.rs b/crates/shirabe/src/package/locker.rs index ecd96e3..9522e55 100644 --- a/crates/shirabe/src/package/locker.rs +++ b/crates/shirabe/src/package/locker.rs @@ -967,7 +967,7 @@ impl Locker { .find_packages_with_replacers_and_providers( &link.get_target(), Some(FindPackageConstraint::Constraint( - link.get_constraint().clone_box(), + link.get_constraint().clone(), )), ) .is_empty() diff --git a/crates/shirabe/src/package/version/version_bumper.rs b/crates/shirabe/src/package/version/version_bumper.rs index a9b5a67..8cc4b5d 100644 --- a/crates/shirabe/src/package/version/version_bumper.rs +++ b/crates/shirabe/src/package/version/version_bumper.rs @@ -8,7 +8,7 @@ use crate::util::Platform; use anyhow::Result; use indexmap::IndexMap; use shirabe_external_packages::composer::pcre::{CaptureKey, Preg}; -use shirabe_semver::constraint::ConstraintInterface; +use shirabe_semver::constraint::AnyConstraint; use shirabe_semver::intervals::Intervals; #[derive(Debug)] @@ -17,7 +17,7 @@ pub struct VersionBumper; impl VersionBumper { pub fn bump_requirement( &self, - constraint: &dyn ConstraintInterface, + constraint: &AnyConstraint, package: &dyn PackageInterface, ) -> Result<String> { let parser = VersionParser::new(); @@ -114,8 +114,8 @@ impl VersionBumper { } let new_constraint = parser.parse_constraints(&modified)?; - if Intervals::is_subset_of(new_constraint.as_ref(), constraint)? - && Intervals::is_subset_of(constraint, new_constraint.as_ref())? + if Intervals::is_subset_of(&new_constraint, constraint)? + && Intervals::is_subset_of(constraint, &new_constraint)? { return Ok(pretty_constraint); } diff --git a/crates/shirabe/src/package/version/version_parser.rs b/crates/shirabe/src/package/version/version_parser.rs index 46a2356..1b588bc 100644 --- a/crates/shirabe/src/package/version/version_parser.rs +++ b/crates/shirabe/src/package/version/version_parser.rs @@ -1,16 +1,16 @@ //! ref: composer/src/Composer/Package/Version/VersionParser.php use indexmap::IndexMap; -use std::sync::{Arc, LazyLock, Mutex}; +use std::sync::{LazyLock, Mutex}; use shirabe_external_packages::composer::pcre::Preg; -use shirabe_semver::constraint::ConstraintInterface; +use shirabe_semver::constraint::AnyConstraint; use shirabe_semver::semver::Semver; use shirabe_semver::version_parser::VersionParser as SemverVersionParser; use crate::repository::PlatformRepository; -static CONSTRAINTS: LazyLock<Mutex<IndexMap<String, Arc<dyn ConstraintInterface + Send + Sync>>>> = +static CONSTRAINTS: LazyLock<Mutex<IndexMap<String, AnyConstraint>>> = LazyLock::new(|| Mutex::new(IndexMap::new())); #[derive(Debug, Clone)] @@ -21,12 +21,19 @@ pub struct VersionParser { impl VersionParser { pub const DEFAULT_BRANCH_ALIAS: &'static str = "9999999-dev"; - pub fn parse_constraints( - &self, - constraints: &str, - ) -> anyhow::Result<Box<dyn ConstraintInterface>> { - // TODO(phase-b): re-introduce a memoization cache once trait objects are Send+Sync. - self.inner.parse_constraints(constraints) + pub fn parse_constraints(&self, constraints: &str) -> anyhow::Result<AnyConstraint> { + { + let cache = CONSTRAINTS.lock().unwrap(); + if let Some(cached) = cache.get(constraints) { + return Ok(cached.clone()); + } + } + let parsed = self.inner.parse_constraints(constraints)?; + CONSTRAINTS + .lock() + .unwrap() + .insert(constraints.to_string(), parsed.clone()); + Ok(parsed) } pub fn parse_name_version_pairs( diff --git a/crates/shirabe/src/package/version/version_selector.rs b/crates/shirabe/src/package/version/version_selector.rs index 832f64c..3bd0191 100644 --- a/crates/shirabe/src/package/version/version_selector.rs +++ b/crates/shirabe/src/package/version/version_selector.rs @@ -8,8 +8,8 @@ use shirabe_external_packages::composer::pcre::Preg; use shirabe_php_shim::{ PHP_MAJOR_VERSION, PHP_MINOR_VERSION, PHP_RELEASE_VERSION, strtolower, version_compare, }; -use shirabe_semver::constraint::Constraint; -use shirabe_semver::constraint::ConstraintInterface; +use shirabe_semver::constraint::AnyConstraint; +use shirabe_semver::constraint::SimpleConstraint; use crate::filter::platform_requirement_filter::IgnoreAllPlatformRequirementFilter; use crate::filter::platform_requirement_filter::IgnoreListPlatformRequirementFilter; @@ -29,7 +29,7 @@ use crate::repository::RepositorySet; #[derive(Debug)] pub struct VersionSelector { repository_set: RepositorySet, - platform_constraints: IndexMap<String, Vec<Box<dyn ConstraintInterface>>>, + platform_constraints: IndexMap<String, Vec<AnyConstraint>>, parser: Option<VersionParser>, } @@ -38,16 +38,19 @@ impl VersionSelector { repository_set: RepositorySet, platform_repo: Option<&crate::repository::PlatformRepository>, ) -> anyhow::Result<Self> { - let mut platform_constraints: IndexMap<String, Vec<Box<dyn ConstraintInterface>>> = - IndexMap::new(); + let mut platform_constraints: IndexMap<String, Vec<AnyConstraint>> = IndexMap::new(); if let Some(platform_repo) = platform_repo { for package in <PlatformRepository as RepositoryInterface>::get_packages(platform_repo) { - let constraint = Constraint::new("==", package.get_version()); + let constraint = SimpleConstraint::new( + "==".to_string(), + package.get_version().to_string(), + None, + ); platform_constraints .entry(package.get_name().to_string()) .or_default() - .push(Box::new(constraint)); + .push(constraint.into()); } } Ok(Self { @@ -90,7 +93,7 @@ impl VersionSelector { }; let mut candidates = self.repository_set.find_packages( &strtolower(package_name), - constraint.as_ref().map(|c| c.clone_box()), + constraint.as_ref().map(|c| c.clone()), repo_set_flags, ); @@ -142,7 +145,7 @@ impl VersionSelector { let reason; if let Some(provided_constraints) = self.platform_constraints.get(name) { for provided_constraint in provided_constraints { - if link.get_constraint().matches(provided_constraint.as_ref()) { + if link.get_constraint().matches(provided_constraint) { continue 'reqs; } let list_filter_opt = platform_requirement_filter @@ -154,10 +157,10 @@ impl VersionSelector { if list_filter.is_upper_bound_ignored(name) { let filtered_constraint = list_filter.filter_constraint( name, - link.get_constraint().clone_box(), + link.get_constraint().clone(), false, )?; - if filtered_constraint.matches(provided_constraint.as_ref()) { + if filtered_constraint.matches(provided_constraint) { continue 'reqs; } } diff --git a/crates/shirabe/src/plugin/plugin_manager.rs b/crates/shirabe/src/plugin/plugin_manager.rs index 26e6296..77ac577 100644 --- a/crates/shirabe/src/plugin/plugin_manager.rs +++ b/crates/shirabe/src/plugin/plugin_manager.rs @@ -13,7 +13,8 @@ use shirabe_php_shim::{ is_array, is_string, ksort, preg_quote, str_replace, strrpos, strtr, substr, trigger_error, trim, var_export, var_export_str, version_compare, }; -use shirabe_semver::constraint::Constraint; +use shirabe_semver::constraint::AnyConstraint; +use shirabe_semver::constraint::SimpleConstraint; use crate::composer::PartialComposerHandle; use crate::composer::{ComposerHandle, ComposerWeakHandle}; @@ -227,9 +228,7 @@ impl PluginManager { if package.get_type() == "composer-plugin" { let requires_map = package.get_requires(); - let mut requires_composer: Option< - &dyn shirabe_semver::constraint::ConstraintInterface, - > = None; + let mut requires_composer: Option<&shirabe_semver::constraint::AnyConstraint> = None; for (_k, link) in &requires_map { if "composer-plugin-api" == link.get_target() { requires_composer = Some(link.get_constraint()); @@ -248,15 +247,17 @@ impl PluginManager { }; let current_plugin_api_version = self.get_plugin_api_version(); - let current_plugin_api_constraint = Constraint::new( - "==", + let current_plugin_api_constraint = SimpleConstraint::new( + "==".to_string(), self.version_parser - .normalize(¤t_plugin_api_version, None)?, + .normalize(¤t_plugin_api_version, None)? + .to_string(), + None, ); if requires_composer.get_pretty_string() == self.get_plugin_api_version() { self.io.write_error(&format!("<warning>The \"{}\" plugin requires composer-plugin-api {}, this *WILL* break in the future and it should be fixed ASAP (require ^{} instead for example).</warning>", package.get_name(), self.get_plugin_api_version(), self.get_plugin_api_version())); - } else if !requires_composer.matches(¤t_plugin_api_constraint) { + } else if !requires_composer.matches(¤t_plugin_api_constraint.into()) { self.io.write_error(&format!("<warning>The \"{}\" plugin {}was skipped because it requires a Plugin API version (\"{}\") that does not match your Composer installation (\"{}\"). You may need to run composer update with the \"--no-plugins\" option.</warning>", package.get_name(), if is_global_plugin || self.running_in_global_dir { "(installed globally) " } else { "" }, diff --git a/crates/shirabe/src/repository/advisory_provider_interface.rs b/crates/shirabe/src/repository/advisory_provider_interface.rs index 4c08a63..0de9c42 100644 --- a/crates/shirabe/src/repository/advisory_provider_interface.rs +++ b/crates/shirabe/src/repository/advisory_provider_interface.rs @@ -3,7 +3,7 @@ use crate::advisory::PartialSecurityAdvisory; use crate::advisory::SecurityAdvisory; use indexmap::IndexMap; -use shirabe_semver::constraint::ConstraintInterface; +use shirabe_semver::constraint::AnyConstraint; #[derive(Debug)] pub enum PartialOrSecurityAdvisory { @@ -31,7 +31,7 @@ pub trait AdvisoryProviderInterface { fn get_security_advisories( &self, - package_constraint_map: IndexMap<String, Box<dyn ConstraintInterface>>, + package_constraint_map: IndexMap<String, AnyConstraint>, allow_partial_advisories: bool, ) -> anyhow::Result<SecurityAdvisoryResult>; } diff --git a/crates/shirabe/src/repository/array_repository.rs b/crates/shirabe/src/repository/array_repository.rs index f8d2e0c..fe45849 100644 --- a/crates/shirabe/src/repository/array_repository.rs +++ b/crates/shirabe/src/repository/array_repository.rs @@ -10,8 +10,8 @@ use shirabe_php_shim::{ Countable, InvalidArgumentException, LogicException, implode, preg_quote, spl_object_hash, strtolower, }; -use shirabe_semver::constraint::Constraint; -use shirabe_semver::constraint::ConstraintInterface; +use shirabe_semver::constraint::AnyConstraint; +use shirabe_semver::constraint::SimpleConstraint; use crate::package::AliasPackage; use crate::package::BasePackage; @@ -162,7 +162,7 @@ impl RepositoryInterface for ArrayRepository { fn load_packages( &self, - package_name_map: IndexMap<String, Option<Box<dyn ConstraintInterface>>>, + package_name_map: IndexMap<String, Option<AnyConstraint>>, acceptable_stabilities: IndexMap<String, i64>, stability_flags: IndexMap<String, i64>, already_loaded: IndexMap<String, IndexMap<String, Box<dyn PackageInterface>>>, @@ -178,7 +178,14 @@ impl RepositoryInterface for ArrayRepository { .unwrap(); let constraint_matches = match constraint_opt { None => true, - Some(c) => c.matches(&Constraint::new("==", package.get_version())), + Some(c) => c.matches( + &SimpleConstraint::new( + "==".to_string(), + package.get_version().to_string(), + None, + ) + .into(), + ), }; if constraint_matches && StabilityFilter::is_package_acceptable( @@ -233,18 +240,22 @@ impl RepositoryInterface for ArrayRepository { ) -> Option<Box<dyn BasePackage>> { let name = strtolower(name); - let constraint: Box<dyn ConstraintInterface> = match constraint { + let constraint: AnyConstraint = match constraint { FindPackageConstraint::Constraint(c) => c, FindPackageConstraint::String(s) => { let version_parser = VersionParser::new(); - version_parser.parse_constraints(&s).unwrap().clone_box() + version_parser.parse_constraints(&s).unwrap().clone() } }; for package in self.get_packages() { if name == PackageInterface::get_name(package.as_ref()) { - let pkg_constraint = Constraint::new("==", package.get_version()); - if constraint.matches(&pkg_constraint) { + let pkg_constraint = SimpleConstraint::new( + "==".to_string(), + package.get_version().to_string(), + None, + ); + if constraint.matches(&pkg_constraint.into()) { return Some(package); } } @@ -262,22 +273,26 @@ impl RepositoryInterface for ArrayRepository { let name = strtolower(name); let mut packages = vec![]; - let constraint: Option<Box<dyn ConstraintInterface>> = match constraint { + let constraint: Option<AnyConstraint> = match constraint { None => None, Some(FindPackageConstraint::Constraint(c)) => Some(c), Some(FindPackageConstraint::String(s)) => { let version_parser = VersionParser::new(); - Some(version_parser.parse_constraints(&s).unwrap().clone_box()) + Some(version_parser.parse_constraints(&s).unwrap().clone()) } }; for package in self.get_packages() { if name == PackageInterface::get_name(package.as_ref()) { if constraint.is_none() - || constraint - .as_ref() - .unwrap() - .matches(&Constraint::new("==", package.get_version())) + || constraint.as_ref().unwrap().matches( + &SimpleConstraint::new( + "==".to_string(), + package.get_version().to_string(), + None, + ) + .into(), + ) { packages.push(package); } diff --git a/crates/shirabe/src/repository/composer_repository.rs b/crates/shirabe/src/repository/composer_repository.rs index 4bd5442..9752bc0 100644 --- a/crates/shirabe/src/repository/composer_repository.rs +++ b/crates/shirabe/src/repository/composer_repository.rs @@ -11,9 +11,9 @@ use shirabe_php_shim::{ }; use shirabe_semver::compiling_matcher::CompilingMatcher; -use shirabe_semver::constraint::Constraint; -use shirabe_semver::constraint::ConstraintInterface; +use shirabe_semver::constraint::AnyConstraint; use shirabe_semver::constraint::MatchAllConstraint; +use shirabe_semver::constraint::SimpleConstraint; use crate::advisory::PartialSecurityAdvisory; use crate::cache::Cache; @@ -327,11 +327,11 @@ impl ComposerRepository { let has_providers = self.has_providers()?; let name = strtolower(&name); - let constraint: Box<dyn ConstraintInterface> = match constraint { - PhpMixed::String(s) => self.version_parser.parse_constraints(&s)?.clone_box(), + let constraint: AnyConstraint = match constraint { + PhpMixed::String(s) => self.version_parser.parse_constraints(&s)?.clone(), _ => { // already a ConstraintInterface object passed as opaque PhpMixed - self.version_parser.parse_constraints("")?.clone_box() + self.version_parser.parse_constraints("")?.clone() } }; @@ -345,7 +345,7 @@ impl ComposerRepository { let packages = self.what_provides(&name, None, None, IndexMap::new())?; let packages_vec: Vec<Box<dyn BasePackage>> = packages.into_values().collect(); return Ok( - match self.filter_packages(packages_vec, Some(&*constraint), true) { + match self.filter_packages(packages_vec, Some(&constraint), true) { FindPackageReturn::Package(p) => Some(p), _ => None, }, @@ -356,7 +356,7 @@ impl ComposerRepository { return Ok(None); } - let mut map: IndexMap<String, Option<Box<dyn ConstraintInterface>>> = IndexMap::new(); + let mut map: IndexMap<String, Option<AnyConstraint>> = IndexMap::new(); map.insert(name.clone(), Some(constraint)); let packages = self.load_async_packages(map, None, None, IndexMap::new())?; @@ -374,7 +374,7 @@ impl ComposerRepository { self.what_provides(&provider_name, None, None, IndexMap::new())?; let packages_vec: Vec<Box<dyn BasePackage>> = packages.into_values().collect(); return Ok( - match self.filter_packages(packages_vec, Some(&*constraint), true) { + match self.filter_packages(packages_vec, Some(&constraint), true) { FindPackageReturn::Package(p) => Some(p), _ => None, }, @@ -401,11 +401,9 @@ impl ComposerRepository { let has_providers = self.has_providers()?; let name = strtolower(&name); - let constraint: Option<Box<dyn ConstraintInterface>> = match constraint { + let constraint: Option<AnyConstraint> = match constraint { None => None, - Some(PhpMixed::String(s)) => { - Some(self.version_parser.parse_constraints(&s)?.clone_box()) - } + Some(PhpMixed::String(s)) => Some(self.version_parser.parse_constraints(&s)?.clone()), Some(_) => None, }; @@ -419,7 +417,7 @@ impl ComposerRepository { let packages = self.what_provides(&name, None, None, IndexMap::new())?; let packages_vec: Vec<Box<dyn BasePackage>> = packages.into_values().collect(); return Ok( - match self.filter_packages(packages_vec, constraint.as_deref(), false) { + match self.filter_packages(packages_vec, constraint.as_ref(), false) { FindPackageReturn::Packages(v) => v, _ => vec![], }, @@ -430,7 +428,7 @@ impl ComposerRepository { return Ok(vec![]); } - let mut map: IndexMap<String, Option<Box<dyn ConstraintInterface>>> = IndexMap::new(); + let mut map: IndexMap<String, Option<AnyConstraint>> = IndexMap::new(); map.insert(name.clone(), constraint); let result = self.load_async_packages(map, None, None, IndexMap::new())?; @@ -444,7 +442,7 @@ impl ComposerRepository { self.what_provides(&provider_name, None, None, IndexMap::new())?; let packages_vec: Vec<Box<dyn BasePackage>> = packages.into_values().collect(); return Ok( - match self.filter_packages(packages_vec, constraint.as_deref(), false) { + match self.filter_packages(packages_vec, constraint.as_ref(), false) { FindPackageReturn::Packages(v) => v, _ => vec![], }, @@ -464,7 +462,7 @@ impl ComposerRepository { fn filter_packages( &self, packages: Vec<Box<dyn BasePackage>>, - constraint: Option<&dyn ConstraintInterface>, + constraint: Option<&AnyConstraint>, return_first_match: bool, ) -> FindPackageReturn { if constraint.is_none() { @@ -482,9 +480,10 @@ impl ComposerRepository { let mut filtered_packages: Vec<Box<dyn BasePackage>> = Vec::new(); for package in packages.into_iter() { - let pkg_constraint = Constraint::new("==", package.get_version().to_string()); + let pkg_constraint = + SimpleConstraint::new("==".to_string(), package.get_version().to_string(), None); - if constraint.matches(&pkg_constraint) { + if constraint.matches(&pkg_constraint.into()) { if return_first_match { return FindPackageReturn::Package(package); } @@ -506,15 +505,10 @@ impl ComposerRepository { if self.lazy_providers_url.is_some() { if let Some(ref available_packages) = self.available_packages.clone() { if self.available_package_patterns.is_none() { - let mut package_map: IndexMap<String, Option<Box<dyn ConstraintInterface>>> = - IndexMap::new(); + let mut package_map: IndexMap<String, Option<AnyConstraint>> = IndexMap::new(); for name in available_packages.values() { - package_map.insert( - name.clone(), - Some( - Box::new(MatchAllConstraint::new()) as Box<dyn ConstraintInterface> - ), - ); + package_map + .insert(name.clone(), Some(MatchAllConstraint::new(None).into())); } let result = @@ -715,7 +709,7 @@ impl ComposerRepository { pub fn load_packages( &mut self, - mut package_name_map: IndexMap<String, Option<Box<dyn ConstraintInterface>>>, + mut package_name_map: IndexMap<String, Option<AnyConstraint>>, acceptable_stabilities: IndexMap<String, i64>, stability_flags: IndexMap<String, i64>, already_loaded: IndexMap<String, IndexMap<String, Box<dyn PackageInterface>>>, @@ -767,7 +761,7 @@ impl ComposerRepository { )?; let constraint = package_name_map .get(&name) - .and_then(|c| c.as_ref().map(|c| c.clone_box())); + .and_then(|c| c.as_ref().map(|c| c.clone())); for (_uid, candidate) in candidates.iter() { if candidate.get_name() != name { return Err(LogicException { @@ -780,8 +774,12 @@ impl ComposerRepository { let matches_constraint = match &constraint { None => true, Some(c) => { - let pkg_c = Constraint::new("==", candidate.get_version().to_string()); - c.matches(&pkg_c) + let pkg_c = SimpleConstraint::new( + "==".to_string(), + candidate.get_version().to_string(), + None, + ); + c.matches(&pkg_c.into()) } }; if matches_constraint { @@ -1025,7 +1023,7 @@ impl ComposerRepository { /// @inheritDoc pub fn get_security_advisories( &mut self, - mut package_constraint_map: IndexMap<String, Box<dyn ConstraintInterface>>, + mut package_constraint_map: IndexMap<String, AnyConstraint>, allow_partial_advisories: bool, ) -> anyhow::Result<SecurityAdvisoryResult> { self.load_root_server_file(Some(600))?; @@ -1059,7 +1057,7 @@ impl ComposerRepository { let repo_name = self.get_repo_name(); let create = |data: &IndexMap<String, PhpMixed>, name: &str, - package_constraint_map: &IndexMap<String, Box<dyn ConstraintInterface>>| + package_constraint_map: &IndexMap<String, AnyConstraint>| -> anyhow::Result<Option<PartialOrSecurityAdvisory>> { let advisory = PartialSecurityAdvisory::create(name, data, &semver_parser)?; let is_full = matches!(advisory, PartialOrSecurityAdvisory::Full(_)); @@ -1081,11 +1079,11 @@ impl ComposerRepository { } .into()); } - let affected_versions: &dyn ConstraintInterface = match &advisory { - PartialOrSecurityAdvisory::Partial(p) => &*p.affected_versions, + let affected_versions: &AnyConstraint = match &advisory { + PartialOrSecurityAdvisory::Partial(p) => &p.affected_versions, PartialOrSecurityAdvisory::Full(p) => p.affected_versions(), }; - let constraint = package_constraint_map.get(name).map(|c| &**c); + let constraint = package_constraint_map.get(name); if let Some(c) = constraint { if !affected_versions.matches(c) { return Ok(None); @@ -1795,7 +1793,7 @@ impl ComposerRepository { /// @param packageNames array of package name => ConstraintInterface|null - if a constraint is provided, only packages matching it will be loaded fn load_async_packages( &mut self, - mut package_names: IndexMap<String, Option<Box<dyn ConstraintInterface>>>, + mut package_names: IndexMap<String, Option<AnyConstraint>>, acceptable_stabilities: Option<&IndexMap<String, i64>>, stability_flags: Option<&IndexMap<String, i64>>, already_loaded: IndexMap<String, IndexMap<String, Box<dyn PackageInterface>>>, @@ -1820,7 +1818,7 @@ impl ComposerRepository { for name in names_snapshot { let constraint = package_names .get(&name) - .and_then(|c| c.as_ref().map(|c| c.clone_box())); + .and_then(|c| c.as_ref().map(|c| c.clone())); if acceptable_stabilities.is_none() || stability_flags.is_none() || StabilityFilter::is_package_acceptable( @@ -1840,11 +1838,10 @@ impl ComposerRepository { } } - let names_iter: Vec<(String, Option<Box<dyn ConstraintInterface>>)> = package_names + let names_iter: Vec<(String, Option<AnyConstraint>)> = package_names .iter() .map(|(k, v)| { - let cloned: Option<Box<dyn ConstraintInterface>> = - v.as_ref().map(|c| dyn_clone_constraint(&**c)); + let cloned: Option<AnyConstraint> = v.clone(); (k.clone(), cloned) }) .collect(); @@ -1972,7 +1969,7 @@ impl ComposerRepository { } let acceptable = ComposerRepository::is_version_acceptable_static( - constraint.as_deref(), + constraint.as_ref(), &real_name, &version, acceptable_stabilities, @@ -2093,7 +2090,7 @@ impl ComposerRepository { /// @param name package name (must be lowercased already) fn is_version_acceptable( &self, - constraint: Option<&dyn ConstraintInterface>, + constraint: Option<&AnyConstraint>, name: &str, version_data: &IndexMap<String, PhpMixed>, acceptable_stabilities: Option<&IndexMap<String, i64>>, @@ -2110,7 +2107,7 @@ impl ComposerRepository { } fn is_version_acceptable_static( - constraint: Option<&dyn ConstraintInterface>, + constraint: Option<&AnyConstraint>, name: &str, version_data: &IndexMap<String, PhpMixed>, acceptable_stabilities: Option<&IndexMap<String, i64>>, @@ -2128,7 +2125,7 @@ impl ComposerRepository { fn is_version_acceptable_with_loader( loader: &ArrayLoader, - constraint: Option<&dyn ConstraintInterface>, + constraint: Option<&AnyConstraint>, name: &str, version_data: &IndexMap<String, PhpMixed>, acceptable_stabilities: Option<&IndexMap<String, i64>>, @@ -2160,7 +2157,7 @@ impl ComposerRepository { } if let Some(c) = constraint { - if !CompilingMatcher::r#match(c, Constraint::OP_EQ, version.clone()) { + if !CompilingMatcher::r#match(c, SimpleConstraint::OP_EQ, version.clone()) { continue; } } @@ -3492,7 +3489,3 @@ fn clone_root_data(rd: &RootData) -> RootData { fn dyn_clone_box(_pkg: &dyn BasePackage) -> Box<dyn BasePackage> { todo!() } - -fn dyn_clone_constraint(_c: &dyn ConstraintInterface) -> Box<dyn ConstraintInterface> { - todo!() -} diff --git a/crates/shirabe/src/repository/composite_repository.rs b/crates/shirabe/src/repository/composite_repository.rs index b3ac1dd..6671895 100644 --- a/crates/shirabe/src/repository/composite_repository.rs +++ b/crates/shirabe/src/repository/composite_repository.rs @@ -3,7 +3,7 @@ use std::any::Any; use indexmap::IndexMap; -use shirabe_semver::constraint::ConstraintInterface; +use shirabe_semver::constraint::AnyConstraint; use crate::package::BasePackage; use crate::package::PackageInterface; @@ -111,7 +111,7 @@ impl RepositoryInterface for CompositeRepository { fn load_packages( &self, - package_name_map: IndexMap<String, Option<Box<dyn ConstraintInterface>>>, + package_name_map: IndexMap<String, Option<AnyConstraint>>, acceptable_stabilities: IndexMap<String, i64>, stability_flags: IndexMap<String, i64>, already_loaded: IndexMap<String, IndexMap<String, Box<dyn PackageInterface>>>, @@ -121,11 +121,10 @@ impl RepositoryInterface for CompositeRepository { for repository in &self.repositories { // TODO(phase-b): manual deep clone since trait objects in maps don't derive Clone. - let name_map_cloned: IndexMap<String, Option<Box<dyn ConstraintInterface>>> = - package_name_map - .iter() - .map(|(k, v)| (k.clone(), v.as_ref().map(|c| c.clone_box()))) - .collect(); + let name_map_cloned: IndexMap<String, Option<AnyConstraint>> = package_name_map + .iter() + .map(|(k, v)| (k.clone(), v.as_ref().map(|c| c.clone()))) + .collect(); let already_loaded_cloned: IndexMap< String, IndexMap<String, Box<dyn PackageInterface>>, diff --git a/crates/shirabe/src/repository/filter_repository.rs b/crates/shirabe/src/repository/filter_repository.rs index 8a0e6f6..a3cb18b 100644 --- a/crates/shirabe/src/repository/filter_repository.rs +++ b/crates/shirabe/src/repository/filter_repository.rs @@ -10,7 +10,7 @@ use anyhow::Result; use indexmap::IndexMap; use shirabe_external_packages::composer::pcre::Preg; use shirabe_php_shim::{InvalidArgumentException, PhpMixed}; -use shirabe_semver::constraint::ConstraintInterface; +use shirabe_semver::constraint::AnyConstraint; #[derive(Debug)] pub struct FilterRepository { @@ -187,7 +187,7 @@ impl RepositoryInterface for FilterRepository { fn load_packages( &self, - mut package_name_map: IndexMap<String, Option<Box<dyn ConstraintInterface>>>, + mut package_name_map: IndexMap<String, Option<AnyConstraint>>, acceptable_stabilities: IndexMap<String, i64>, stability_flags: IndexMap<String, i64>, already_loaded: IndexMap<String, IndexMap<String, Box<dyn PackageInterface>>>, @@ -272,7 +272,7 @@ impl AdvisoryProviderInterface for FilterRepository { fn get_security_advisories( &self, - mut package_constraint_map: IndexMap<String, Box<dyn ConstraintInterface>>, + mut package_constraint_map: IndexMap<String, AnyConstraint>, allow_partial_advisories: bool, ) -> anyhow::Result<SecurityAdvisoryResult> { if let Some(advisory_repo) = self.repo.as_advisory_provider() { diff --git a/crates/shirabe/src/repository/installed_array_repository.rs b/crates/shirabe/src/repository/installed_array_repository.rs index fd273b9..b089df6 100644 --- a/crates/shirabe/src/repository/installed_array_repository.rs +++ b/crates/shirabe/src/repository/installed_array_repository.rs @@ -2,7 +2,7 @@ use indexmap::IndexMap; use shirabe_php_shim::Countable; -use shirabe_semver::constraint::ConstraintInterface; +use shirabe_semver::constraint::AnyConstraint; use crate::package::BasePackage; use crate::package::PackageInterface; @@ -114,7 +114,7 @@ impl RepositoryInterface for InstalledArrayRepository { } fn load_packages( &self, - _package_name_map: IndexMap<String, Option<Box<dyn ConstraintInterface>>>, + _package_name_map: IndexMap<String, Option<AnyConstraint>>, _acceptable_stabilities: IndexMap<String, i64>, _stability_flags: IndexMap<String, i64>, _already_loaded: IndexMap<String, IndexMap<String, Box<dyn PackageInterface>>>, diff --git a/crates/shirabe/src/repository/installed_filesystem_repository.rs b/crates/shirabe/src/repository/installed_filesystem_repository.rs index db1428a..5c6fecf 100644 --- a/crates/shirabe/src/repository/installed_filesystem_repository.rs +++ b/crates/shirabe/src/repository/installed_filesystem_repository.rs @@ -3,7 +3,7 @@ use anyhow::Result; use indexmap::IndexMap; use shirabe_php_shim::Countable; -use shirabe_semver::constraint::ConstraintInterface; +use shirabe_semver::constraint::AnyConstraint; use crate::json::JsonFile; use crate::package::BasePackage; @@ -124,7 +124,7 @@ impl RepositoryInterface for InstalledFilesystemRepository { } fn load_packages( &self, - _package_name_map: IndexMap<String, Option<Box<dyn ConstraintInterface>>>, + _package_name_map: IndexMap<String, Option<AnyConstraint>>, _acceptable_stabilities: IndexMap<String, i64>, _stability_flags: IndexMap<String, i64>, _already_loaded: IndexMap<String, IndexMap<String, Box<dyn PackageInterface>>>, diff --git a/crates/shirabe/src/repository/installed_repository.rs b/crates/shirabe/src/repository/installed_repository.rs index 3163ed2..5c9e0d7 100644 --- a/crates/shirabe/src/repository/installed_repository.rs +++ b/crates/shirabe/src/repository/installed_repository.rs @@ -2,9 +2,9 @@ use indexmap::IndexMap; use shirabe_php_shim::LogicException; -use shirabe_semver::constraint::Constraint; -use shirabe_semver::constraint::ConstraintInterface; +use shirabe_semver::constraint::AnyConstraint; use shirabe_semver::constraint::MatchAllConstraint; +use shirabe_semver::constraint::SimpleConstraint; use crate::package::BasePackage; use crate::package::Link; @@ -56,7 +56,7 @@ impl InstalledRepository { ) -> Vec<Box<dyn BasePackage>> { let name = name.to_lowercase(); - let constraint: Option<Box<dyn ConstraintInterface>> = match constraint { + let constraint: Option<AnyConstraint> = match constraint { None => None, Some(FindPackageConstraint::Constraint(c)) => Some(c), Some(FindPackageConstraint::String(s)) => { @@ -70,10 +70,14 @@ impl InstalledRepository { 'candidates: for candidate in repo.get_packages() { if name == candidate.get_name() { if constraint.is_none() - || constraint - .as_ref() - .unwrap() - .matches(&Constraint::new("==", candidate.get_version())) + || constraint.as_ref().unwrap().matches( + &SimpleConstraint::new( + "==".to_string(), + candidate.get_version().to_string(), + None, + ) + .into(), + ) { matches.push(candidate); } @@ -107,7 +111,7 @@ impl InstalledRepository { pub fn get_dependents( &self, needle: NeedleInput, - constraint: Option<Box<dyn ConstraintInterface>>, + constraint: Option<AnyConstraint>, invert: bool, recurse: bool, packages_found: Option<Vec<String>>, @@ -142,9 +146,7 @@ impl InstalledRepository { for needle in &needles_snapshot { if link.get_source() == needle.as_str() { if constraint.is_none() - || link - .get_constraint() - .matches(constraint.as_ref().unwrap().as_ref()) + || link.get_constraint().matches(constraint.as_ref().unwrap()) { if packages_in_tree.contains(&link.get_target().to_string()) { results.push(DependentsEntry( @@ -187,9 +189,9 @@ impl InstalledRepository { for link in links.values() { for needle in &needles { if link.get_target() == needle.as_str() { - let matches_constraint = constraint.as_ref().map_or(true, |c| { - link.get_constraint().matches(c.as_ref()) == !invert - }); + let matches_constraint = constraint + .as_ref() + .map_or(true, |c| link.get_constraint().matches(c) == !invert); if constraint.is_none() || matches_constraint { if packages_in_tree.contains(&link.get_source().to_string()) { results.push(DependentsEntry( @@ -224,8 +226,12 @@ impl InstalledRepository { if invert && needles.contains(&package.get_name().to_string()) { for link in package.get_conflicts().values() { for pkg in self.find_packages(link.get_target(), None) { - let version = Constraint::new("=", pkg.get_version()); - if link.get_constraint().matches(&version) == invert { + let version = SimpleConstraint::new( + "=".to_string(), + pkg.get_version().to_string(), + None, + ); + if link.get_constraint().matches(&version.into()) == invert { results.push(DependentsEntry(package.clone_box(), link.clone(), None)); } } @@ -235,8 +241,12 @@ impl InstalledRepository { for link in package.get_conflicts().values() { if needles.contains(&link.get_target().to_string()) { for pkg in self.find_packages(link.get_target(), None) { - let version = Constraint::new("=", pkg.get_version()); - if link.get_constraint().matches(&version) == invert { + let version = SimpleConstraint::new( + "=".to_string(), + pkg.get_version().to_string(), + None, + ); + if link.get_constraint().matches(&version.into()) == invert { results.push(DependentsEntry(package.clone_box(), link.clone(), None)); } } @@ -246,19 +256,21 @@ impl InstalledRepository { if invert && constraint.is_some() && needles.contains(&package.get_name().to_string()) - && constraint - .as_ref() - .unwrap() - .matches(&Constraint::new("=", package.get_version())) + && constraint.as_ref().unwrap().matches( + &SimpleConstraint::new( + "=".to_string(), + package.get_version().to_string(), + None, + ) + .into(), + ) { 'requires: for link in package.get_requires().values() { if PlatformRepository::is_platform_package(link.get_target()) { if self .find_package( link.get_target(), - FindPackageConstraint::Constraint( - link.get_constraint().clone_box(), - ), + FindPackageConstraint::Constraint(link.get_constraint().clone()), ) .is_some() { @@ -278,7 +290,7 @@ impl InstalledRepository { Link::new( package.get_name().to_string(), link.get_target().to_string(), - Box::new(MatchAllConstraint::new()), + MatchAllConstraint::new(None).into(), Some(Link::TYPE_REQUIRE.to_string()), Some(format!( "{} {}", @@ -297,8 +309,12 @@ impl InstalledRepository { continue; } - let mut version: Box<dyn ConstraintInterface> = - Box::new(Constraint::new("=", pkg.get_version())); + let mut version: AnyConstraint = SimpleConstraint::new( + "=".to_string(), + pkg.get_version().to_string(), + None, + ) + .into(); if link.get_target() != pkg.get_name() { let mut replaces_and_provides: IndexMap<String, Link> = @@ -308,13 +324,13 @@ impl InstalledRepository { } for prov in replaces_and_provides.values() { if link.get_target() == prov.get_target() { - version = prov.get_constraint().clone_box(); + version = prov.get_constraint().clone(); break; } } } - if !link.get_constraint().matches(version.as_ref()) { + if !link.get_constraint().matches(&version) { if let Some(root_pkg) = root_package.as_ref() { let mut root_reqs: IndexMap<String, Link> = root_pkg.get_requires(); for (k, v) in root_pkg.get_dev_requires() { @@ -350,7 +366,7 @@ impl InstalledRepository { Link::new( root_pkg.get_name().to_string(), link.get_target().to_string(), - Box::new(MatchAllConstraint::new()), + MatchAllConstraint::new(None).into(), Some(Link::TYPE_DOES_NOT_REQUIRE.to_string()), Some(format!( "but {} is installed", @@ -446,7 +462,7 @@ impl RepositoryInterface for InstalledRepository { fn load_packages( &self, - package_name_map: IndexMap<String, Option<Box<dyn ConstraintInterface>>>, + package_name_map: IndexMap<String, Option<AnyConstraint>>, acceptable_stabilities: IndexMap<String, i64>, stability_flags: IndexMap<String, i64>, already_loaded: IndexMap<String, IndexMap<String, Box<dyn PackageInterface>>>, diff --git a/crates/shirabe/src/repository/lock_array_repository.rs b/crates/shirabe/src/repository/lock_array_repository.rs index 295d982..57abe56 100644 --- a/crates/shirabe/src/repository/lock_array_repository.rs +++ b/crates/shirabe/src/repository/lock_array_repository.rs @@ -9,7 +9,7 @@ use crate::repository::{ }; use indexmap::IndexMap; use shirabe_php_shim::Countable; -use shirabe_semver::constraint::ConstraintInterface; +use shirabe_semver::constraint::AnyConstraint; #[derive(Debug)] pub struct LockArrayRepository { @@ -61,7 +61,7 @@ impl RepositoryInterface for LockArrayRepository { fn load_packages( &self, - package_name_map: IndexMap<String, Option<Box<dyn ConstraintInterface>>>, + package_name_map: IndexMap<String, Option<AnyConstraint>>, acceptable_stabilities: IndexMap<String, i64>, stability_flags: IndexMap<String, i64>, already_loaded: IndexMap<String, IndexMap<String, Box<dyn PackageInterface>>>, diff --git a/crates/shirabe/src/repository/package_repository.rs b/crates/shirabe/src/repository/package_repository.rs index 6e8186c..93c7e31 100644 --- a/crates/shirabe/src/repository/package_repository.rs +++ b/crates/shirabe/src/repository/package_repository.rs @@ -13,7 +13,7 @@ use crate::repository::{ use indexmap::IndexMap; use shirabe_external_packages::composer::pcre::Preg; use shirabe_php_shim::{Exception, PhpMixed, RuntimeException, var_export}; -use shirabe_semver::constraint::ConstraintInterface; +use shirabe_semver::constraint::AnyConstraint; #[derive(Debug)] pub struct PackageRepository { @@ -91,7 +91,7 @@ impl AdvisoryProviderInterface for PackageRepository { fn get_security_advisories( &self, - package_constraint_map: IndexMap<String, Box<dyn ConstraintInterface>>, + package_constraint_map: IndexMap<String, AnyConstraint>, allow_partial_advisories: bool, ) -> anyhow::Result<SecurityAdvisoryResult> { let parser = VersionParser::new(); diff --git a/crates/shirabe/src/repository/platform_repository.rs b/crates/shirabe/src/repository/platform_repository.rs index 8494e25..0b3ccb1 100644 --- a/crates/shirabe/src/repository/platform_repository.rs +++ b/crates/shirabe/src/repository/platform_repository.rs @@ -11,7 +11,8 @@ use shirabe_php_shim::{ array_slice_strs, explode, get_class, implode, in_array, is_string, sprintf, str_replace, str_starts_with, strpos, strtolower, var_export, }; -use shirabe_semver::constraint::Constraint; +use shirabe_semver::constraint::AnyConstraint; +use shirabe_semver::constraint::SimpleConstraint; use crate::composer; use crate::composer::ComposerHandle; @@ -1759,7 +1760,7 @@ impl PlatformRepository { Link::new( "ext-uuid".to_string(), "lib-uuid".to_string(), - Box::new(Constraint::new("=", &version)), + SimpleConstraint::new("=".to_string(), version.to_string(), None).into(), Some(Link::TYPE_REPLACE.to_string()), Some(ext.get_pretty_version().to_string()), ), @@ -1824,7 +1825,7 @@ impl PlatformRepository { Link::new( format!("lib-{}", name), format!("lib-{}", replace_lower), - Box::new(Constraint::new("=", &version)), + SimpleConstraint::new("=".to_string(), version.to_string(), None).into(), Some(Link::TYPE_REPLACE.to_string()), Some(lib.get_pretty_version().to_string()), ), @@ -1838,7 +1839,7 @@ impl PlatformRepository { Link::new( format!("lib-{}", name), format!("lib-{}", provide_lower), - Box::new(Constraint::new("=", &version)), + SimpleConstraint::new("=".to_string(), version.to_string(), None).into(), Some(Link::TYPE_PROVIDE.to_string()), Some(lib.get_pretty_version().to_string()), ), @@ -1962,10 +1963,7 @@ impl crate::repository::RepositoryInterface for PlatformRepository { fn load_packages( &self, - package_name_map: IndexMap< - String, - Option<Box<dyn shirabe_semver::constraint::ConstraintInterface>>, - >, + package_name_map: IndexMap<String, Option<shirabe_semver::constraint::AnyConstraint>>, acceptable_stabilities: IndexMap<String, i64>, stability_flags: IndexMap<String, i64>, already_loaded: IndexMap<String, IndexMap<String, Box<dyn PackageInterface>>>, diff --git a/crates/shirabe/src/repository/repository_interface.rs b/crates/shirabe/src/repository/repository_interface.rs index d18951b..4bd1f3e 100644 --- a/crates/shirabe/src/repository/repository_interface.rs +++ b/crates/shirabe/src/repository/repository_interface.rs @@ -5,18 +5,18 @@ use crate::package::PackageInterface; use crate::repository::AdvisoryProviderInterface; use indexmap::IndexMap; use shirabe_php_shim::Countable; -use shirabe_semver::constraint::ConstraintInterface; +use shirabe_semver::constraint::AnyConstraint; pub enum FindPackageConstraint { String(String), - Constraint(Box<dyn ConstraintInterface>), + Constraint(AnyConstraint), } impl Clone for FindPackageConstraint { fn clone(&self) -> Self { match self { Self::String(s) => Self::String(s.clone()), - Self::Constraint(c) => Self::Constraint(c.clone_box()), + Self::Constraint(c) => Self::Constraint(c.clone()), } } } @@ -71,7 +71,7 @@ pub trait RepositoryInterface: Countable + std::fmt::Debug { fn load_packages( &self, - package_name_map: IndexMap<String, Option<Box<dyn ConstraintInterface>>>, + package_name_map: IndexMap<String, Option<AnyConstraint>>, acceptable_stabilities: IndexMap<String, i64>, stability_flags: IndexMap<String, i64>, already_loaded: IndexMap<String, IndexMap<String, Box<dyn PackageInterface>>>, diff --git a/crates/shirabe/src/repository/repository_manager.rs b/crates/shirabe/src/repository/repository_manager.rs index 10e3e37..e85312a 100644 --- a/crates/shirabe/src/repository/repository_manager.rs +++ b/crates/shirabe/src/repository/repository_manager.rs @@ -2,7 +2,7 @@ use indexmap::IndexMap; use shirabe_php_shim::{InvalidArgumentException, PhpMixed, json_encode}; -use shirabe_semver::constraint::ConstraintInterface; +use shirabe_semver::constraint::AnyConstraint; use crate::config::Config; use crate::event_dispatcher::EventDispatcher; @@ -51,12 +51,12 @@ impl RepositoryManager { pub fn find_package( &self, name: &str, - constraint: &dyn ConstraintInterface, + constraint: &AnyConstraint, ) -> Option<Box<dyn PackageInterface>> { for repository in &self.repositories { if let Some(package) = repository.find_package( name, - crate::repository::FindPackageConstraint::Constraint(constraint.clone_box()), + crate::repository::FindPackageConstraint::Constraint(constraint.clone()), ) { return Some(package.clone_package_box()); } @@ -67,14 +67,14 @@ impl RepositoryManager { pub fn find_packages( &self, name: &str, - constraint: &dyn ConstraintInterface, + constraint: &AnyConstraint, ) -> Vec<Box<dyn PackageInterface>> { let mut packages: Vec<Box<dyn PackageInterface>> = vec![]; for repository in self.get_repositories() { for p in repository.find_packages( name, Some(crate::repository::FindPackageConstraint::Constraint( - constraint.clone_box(), + constraint.clone(), )), ) { packages.push(p.clone_package_box()); diff --git a/crates/shirabe/src/repository/repository_set.rs b/crates/shirabe/src/repository/repository_set.rs index 84b2424..84159d4 100644 --- a/crates/shirabe/src/repository/repository_set.rs +++ b/crates/shirabe/src/repository/repository_set.rs @@ -8,10 +8,10 @@ use shirabe_php_shim::{ LogicException, PhpMixed, RuntimeException, array_merge, array_merge_recursive, ksort, strtolower, }; -use shirabe_semver::constraint::Constraint; -use shirabe_semver::constraint::ConstraintInterface; +use shirabe_semver::constraint::AnyConstraint; use shirabe_semver::constraint::MatchAllConstraint; use shirabe_semver::constraint::MultiConstraint; +use shirabe_semver::constraint::SimpleConstraint; use crate::advisory::PartialSecurityAdvisory; use crate::advisory::SecurityAdvisory; @@ -79,10 +79,10 @@ pub struct RepositorySet { /// @var ConstraintInterface[] /// @phpstan-var array<string, ConstraintInterface> - pub(crate) root_requires: IndexMap<String, Box<dyn ConstraintInterface>>, + pub(crate) root_requires: IndexMap<String, AnyConstraint>, /// @var array<string, ConstraintInterface> - pub(crate) temporary_constraints: IndexMap<String, Box<dyn ConstraintInterface>>, + pub(crate) temporary_constraints: IndexMap<String, AnyConstraint>, /// @var bool locked: bool, @@ -115,8 +115,8 @@ impl RepositorySet { stability_flags: IndexMap<String, i64>, root_aliases: Vec<RootAliasInput>, root_references: IndexMap<String, String>, - mut root_requires: IndexMap<String, Box<dyn ConstraintInterface>>, - temporary_constraints: IndexMap<String, Box<dyn ConstraintInterface>>, + mut root_requires: IndexMap<String, AnyConstraint>, + temporary_constraints: IndexMap<String, AnyConstraint>, ) -> Self { let root_aliases = Self::get_root_aliases_per_package(root_aliases); @@ -156,12 +156,12 @@ impl RepositorySet { /// @return ConstraintInterface[] an array of package name => constraint from the root package, platform requirements excluded /// @phpstan-return array<string, ConstraintInterface> - pub fn get_root_requires(&self) -> &IndexMap<String, Box<dyn ConstraintInterface>> { + pub fn get_root_requires(&self) -> &IndexMap<String, AnyConstraint> { &self.root_requires } /// @return array<string, ConstraintInterface> Runtime temporary constraints that will be used to filter packages - pub fn get_temporary_constraints(&self) -> &IndexMap<String, Box<dyn ConstraintInterface>> { + pub fn get_temporary_constraints(&self) -> &IndexMap<String, AnyConstraint> { &self.temporary_constraints } @@ -208,7 +208,7 @@ impl RepositorySet { pub fn find_packages( &self, name: &str, - constraint: Option<Box<dyn ConstraintInterface>>, + constraint: Option<AnyConstraint>, flags: i64, ) -> Vec<Box<dyn BasePackage>> { let ignore_stability = (flags & Self::ALLOW_UNACCEPTABLE_STABILITIES) != 0; @@ -220,15 +220,14 @@ impl RepositorySet { // PHP: $repository->findPackages($name, $constraint) ?: [] let constraint_clone = constraint .as_ref() - .map(|c| FindPackageConstraint::Constraint(c.clone_box())); + .map(|c| FindPackageConstraint::Constraint(c.clone())); let found = repository.find_packages(name, constraint_clone); packages.push(found); } } else { 'outer: for repository in &self.repositories { - let mut name_map: IndexMap<String, Option<Box<dyn ConstraintInterface>>> = - IndexMap::new(); - name_map.insert(name.to_string(), constraint.as_ref().map(|c| c.clone_box())); + let mut name_map: IndexMap<String, Option<AnyConstraint>> = IndexMap::new(); + name_map.insert(name.to_string(), constraint.as_ref().map(|c| c.clone())); let acceptable = if ignore_stability { // PHP: BasePackage::STABILITIES crate::package::STABILITIES @@ -290,9 +289,9 @@ impl RepositorySet { allow_partial_advisories: bool, ignore_unreachable: bool, ) -> Result<SecurityAdvisoriesResult> { - let mut map: IndexMap<String, Box<dyn ConstraintInterface>> = IndexMap::new(); + let mut map: IndexMap<String, AnyConstraint> = IndexMap::new(); for name in &package_names { - map.insert(name.clone(), Box::new(MatchAllConstraint::new())); + map.insert(name.clone(), MatchAllConstraint::new(None).into()); } let mut unreachable_repos: Vec<String> = vec![]; @@ -317,7 +316,7 @@ impl RepositorySet { allow_partial_advisories: bool, ignore_unreachable: bool, ) -> Result<SecurityAdvisoriesResult> { - let mut map: IndexMap<String, Box<dyn ConstraintInterface>> = IndexMap::new(); + let mut map: IndexMap<String, AnyConstraint> = IndexMap::new(); for package in packages { // ignore root alias versions as they are not actual package versions and should not matter when it comes to vulnerabilities if let Some(alias) = package.as_any().downcast_ref::<AliasPackage>() { @@ -327,20 +326,29 @@ impl RepositorySet { } let name = package.get_name().to_string(); if map.contains_key(&name) { - // TODO(phase-b): MultiConstraint::new signature let existing = map.shift_remove(&name).unwrap(); map.insert( name, - Box::new(MultiConstraint::new( + MultiConstraint::new( vec![ - Box::new(Constraint::new("=", package.get_version())), + AnyConstraint::Simple(SimpleConstraint::new( + "=".to_string(), + package.get_version().to_string(), + None, + )), existing, ], false, - )), + None, + ) + .into(), ); } else { - map.insert(name, Box::new(Constraint::new("=", package.get_version()))); + map.insert( + name, + SimpleConstraint::new("=".to_string(), package.get_version().to_string(), None) + .into(), + ); } } @@ -363,7 +371,7 @@ impl RepositorySet { /// @return ($allowPartialAdvisories is true ? array<string, array<PartialSecurityAdvisory|SecurityAdvisory>> : array<string, array<SecurityAdvisory>>) fn get_security_advisories_for_constraints( &self, - package_constraint_map: IndexMap<String, Box<dyn ConstraintInterface>>, + package_constraint_map: IndexMap<String, AnyConstraint>, allow_partial_advisories: bool, ignore_unreachable: bool, unreachable_repos: &mut Vec<String>, diff --git a/crates/shirabe/src/repository/root_package_repository.rs b/crates/shirabe/src/repository/root_package_repository.rs index 6213291..1487f3e 100644 --- a/crates/shirabe/src/repository/root_package_repository.rs +++ b/crates/shirabe/src/repository/root_package_repository.rs @@ -61,10 +61,7 @@ impl RepositoryInterface for RootPackageRepository { fn load_packages( &self, - package_name_map: IndexMap< - String, - Option<Box<dyn shirabe_semver::constraint::ConstraintInterface>>, - >, + package_name_map: IndexMap<String, Option<shirabe_semver::constraint::AnyConstraint>>, acceptable_stabilities: IndexMap<String, i64>, stability_flags: IndexMap<String, i64>, already_loaded: IndexMap<String, IndexMap<String, Box<dyn PackageInterface>>>, diff --git a/crates/shirabe/src/repository/vcs_repository.rs b/crates/shirabe/src/repository/vcs_repository.rs index 05dd329..1ae7e40 100644 --- a/crates/shirabe/src/repository/vcs_repository.rs +++ b/crates/shirabe/src/repository/vcs_repository.rs @@ -8,7 +8,8 @@ use shirabe_php_shim::{ InvalidArgumentException, PhpMixed, array_search_mixed, count, get_class, in_array, str_replace, strpos, }; -use shirabe_semver::constraint::Constraint; +use shirabe_semver::constraint::AnyConstraint; +use shirabe_semver::constraint::SimpleConstraint; use crate::config::Config; use crate::downloader::TransportException; @@ -514,9 +515,14 @@ impl VcsRepository { }); if let Some(existing_package) = self.inner.find_package( &tag_package_name, - crate::repository::FindPackageConstraint::Constraint(Box::new( - Constraint::new("=", &version_normalized), - )), + crate::repository::FindPackageConstraint::Constraint( + SimpleConstraint::new( + "=".to_string(), + version_normalized.to_string(), + None, + ) + .into(), + ), ) { if is_very_verbose { self.io.write_error(&format!( @@ -949,10 +955,10 @@ impl VcsRepository { .to_string(); if let Some(existing_package) = self.inner.find_package( &name, - crate::repository::FindPackageConstraint::Constraint(Box::new(Constraint::new( - "=", - &version_normalized, - ))), + crate::repository::FindPackageConstraint::Constraint( + SimpleConstraint::new("=".to_string(), version_normalized.to_string(), None) + .into(), + ), ) { if is_very_verbose { self.io.write_error(&format!( diff --git a/crates/shirabe/src/util/http_downloader.rs b/crates/shirabe/src/util/http_downloader.rs index b1cf921..77d35b1 100644 --- a/crates/shirabe/src/util/http_downloader.rs +++ b/crates/shirabe/src/util/http_downloader.rs @@ -10,7 +10,8 @@ use shirabe_php_shim::{ extension_loaded, file_get_contents, function_exists, implode, is_numeric, max, min, rawurldecode, stream_context_create, stripos, strpos, substr, ucfirst, }; -use shirabe_semver::constraint::Constraint; +use shirabe_semver::constraint::AnyConstraint; +use shirabe_semver::constraint::SimpleConstraint; use crate::composer; use crate::composer::ComposerHandle; @@ -715,11 +716,14 @@ impl HttpDownloader { let version_parser: VersionParser = todo!("VersionParser::new()"); let constraint = version_parser .parse_constraints(versions_value.as_string().unwrap_or(""))?; - let composer_constraint = Constraint::new( - "==", - &version_parser.normalize(&composer::get_version(), None)?, + let composer_constraint = SimpleConstraint::new( + "==".to_string(), + version_parser + .normalize(&composer::get_version(), None)? + .to_string(), + None, ); - if !constraint.matches(&composer_constraint) { + if !constraint.matches(&composer_constraint.into()) { continue; } } @@ -753,11 +757,14 @@ impl HttpDownloader { .and_then(|v| v.as_string()) .unwrap_or(""), )?; - let composer_constraint = Constraint::new( - "==", - &version_parser.normalize(&composer::get_version(), None)?, + let composer_constraint = SimpleConstraint::new( + "==".to_string(), + version_parser + .normalize(&composer::get_version(), None)? + .to_string(), + None, ); - if !constraint.matches(&composer_constraint) { + if !constraint.matches(&composer_constraint.into()) { continue; } |
