aboutsummaryrefslogtreecommitdiffhomepage
path: root/crates/shirabe-semver/src/constraint
diff options
context:
space:
mode:
Diffstat (limited to 'crates/shirabe-semver/src/constraint')
-rw-r--r--crates/shirabe-semver/src/constraint/any_constraint.rs169
-rw-r--r--crates/shirabe-semver/src/constraint/constraint_interface.rs43
-rw-r--r--crates/shirabe-semver/src/constraint/match_all_constraint.rs55
-rw-r--r--crates/shirabe-semver/src/constraint/match_none_constraint.rs41
-rw-r--r--crates/shirabe-semver/src/constraint/multi_constraint.rs196
-rw-r--r--crates/shirabe-semver/src/constraint/simple_constraint.rs (renamed from crates/shirabe-semver/src/constraint/constraint.rs)118
6 files changed, 286 insertions, 336 deletions
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)
}
}