diff options
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; } |
