diff options
Diffstat (limited to 'crates/shirabe-semver/src')
8 files changed, 141 insertions, 176 deletions
diff --git a/crates/shirabe-semver/src/compiling_matcher.rs b/crates/shirabe-semver/src/compiling_matcher.rs index 142fd0d6..0d637037 100644 --- a/crates/shirabe-semver/src/compiling_matcher.rs +++ b/crates/shirabe-semver/src/compiling_matcher.rs @@ -3,25 +3,17 @@ use crate::constraint::AnyConstraint; use crate::constraint::SimpleConstraint; use indexmap::IndexMap; +use shirabe_php_shim::CmpOp; use std::sync::Mutex; use std::sync::OnceLock; +// Rust does not support eval(), so the compiled checker path is always disabled. +// The COMPILED_CHECKER_CACHE is retained structurally but never populated. static COMPILED_CHECKER_CACHE: OnceLock< Mutex<IndexMap<String, Box<dyn Fn(String, bool) -> bool + Send + Sync>>>, > = OnceLock::new(); 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)] = &[ - (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; impl CompilingMatcher { @@ -39,8 +31,13 @@ impl CompilingMatcher { Self::compiled_checker_cache().lock().unwrap().clear(); } - pub fn r#match(constraint: &AnyConstraint, operator: i64, version: String) -> bool { - let result_cache_key = format!("{}{};{}", operator, constraint, version); + pub fn r#match(constraint: &AnyConstraint, operator: CmpOp, version: String) -> bool { + let result_cache_key = format!( + "{}{};{}", + SimpleConstraint::get_operator_constant(operator), + constraint, + version + ); { let cache = Self::result_cache().lock().unwrap(); @@ -49,13 +46,8 @@ impl CompilingMatcher { } } - let trans_op = TRANS_OP_INT - .iter() - .find(|(op, _)| *op == operator) - .map(|(_, s)| *s) - .expect("unknown operator"); let result = - constraint.matches(&SimpleConstraint::new(trans_op.to_string(), version, None).into()); + constraint.matches(&SimpleConstraint::new(operator.to_string(), version, None).into()); Self::result_cache() .lock() diff --git a/crates/shirabe-semver/src/constraint/any_constraint.rs b/crates/shirabe-semver/src/constraint/any_constraint.rs index 37541deb..33c2f7d0 100644 --- a/crates/shirabe-semver/src/constraint/any_constraint.rs +++ b/crates/shirabe-semver/src/constraint/any_constraint.rs @@ -5,6 +5,7 @@ use crate::constraint::MatchAllConstraint; use crate::constraint::MatchNoneConstraint; use crate::constraint::MultiConstraint; use crate::constraint::SimpleConstraint; +use shirabe_php_shim::CmpOp; /// Corresponds to PHP's `ConstraintInterface`. #[derive(Clone, Debug)] @@ -36,7 +37,7 @@ impl AnyConstraint { } } - pub fn compile(&self, other_operator: i64) -> String { + pub fn compile(&self, other_operator: CmpOp) -> String { match self { Self::Simple(c) => c.compile(other_operator), Self::Multi(c) => c.compile(other_operator), @@ -82,10 +83,10 @@ impl AnyConstraint { matches!(self, Self::Simple(_)) } - pub fn get_operator(&self) -> &'static str { + pub fn get_operator(&self) -> Option<CmpOp> { match self { - Self::Simple(c) => c.get_operator(), - _ => "", + Self::Simple(c) => Some(c.get_operator()), + _ => None, } } diff --git a/crates/shirabe-semver/src/constraint/bound.rs b/crates/shirabe-semver/src/constraint/bound.rs index 25904248..6230684d 100644 --- a/crates/shirabe-semver/src/constraint/bound.rs +++ b/crates/shirabe-semver/src/constraint/bound.rs @@ -1,6 +1,7 @@ //! ref: composer/vendor/composer/semver/src/Constraint/Bound.php use anyhow::bail; +use shirabe_php_shim::version_compare_ordering; #[derive(Debug, Clone, PartialEq)] pub struct Bound { @@ -42,7 +43,7 @@ impl Bound { } let compare_result = - shirabe_php_shim::version_compare_2(self.get_version(), other.get_version()); + version_compare_ordering(self.get_version(), other.get_version()) as i8; if compare_result != 0 { return Ok((if operator == ">" { 1 } else { -1 }) == compare_result); diff --git a/crates/shirabe-semver/src/constraint/match_all_constraint.rs b/crates/shirabe-semver/src/constraint/match_all_constraint.rs index 8d4e8705..5bf8ebd9 100644 --- a/crates/shirabe-semver/src/constraint/match_all_constraint.rs +++ b/crates/shirabe-semver/src/constraint/match_all_constraint.rs @@ -1,6 +1,7 @@ //! ref: composer/vendor/composer/semver/src/Constraint/MatchAllConstraint.php use crate::constraint::Bound; +use shirabe_php_shim::CmpOp; #[derive(Debug, Clone, Default)] pub struct MatchAllConstraint { @@ -12,7 +13,7 @@ impl MatchAllConstraint { Self { pretty_string } } - pub fn compile(&self, _other_operator: i64) -> String { + pub fn compile(&self, _other_operator: CmpOp) -> String { "true".to_string() } diff --git a/crates/shirabe-semver/src/constraint/match_none_constraint.rs b/crates/shirabe-semver/src/constraint/match_none_constraint.rs index 08cb3194..fd2b0c6e 100644 --- a/crates/shirabe-semver/src/constraint/match_none_constraint.rs +++ b/crates/shirabe-semver/src/constraint/match_none_constraint.rs @@ -1,6 +1,7 @@ //! ref: composer/vendor/composer/semver/src/Constraint/MatchNoneConstraint.php use crate::constraint::Bound; +use shirabe_php_shim::CmpOp; #[derive(Debug, Clone)] pub struct MatchNoneConstraint { @@ -12,7 +13,7 @@ impl MatchNoneConstraint { Self { pretty_string } } - pub fn compile(&self, _other_operator: i64) -> String { + pub fn compile(&self, _other_operator: CmpOp) -> String { "false".to_string() } diff --git a/crates/shirabe-semver/src/constraint/multi_constraint.rs b/crates/shirabe-semver/src/constraint/multi_constraint.rs index 05cc16e3..4e9b8cf5 100644 --- a/crates/shirabe-semver/src/constraint/multi_constraint.rs +++ b/crates/shirabe-semver/src/constraint/multi_constraint.rs @@ -3,6 +3,7 @@ use crate::constraint::AnyConstraint; use crate::constraint::Bound; use crate::constraint::MatchAllConstraint; +use shirabe_php_shim::CmpOp; #[derive(Debug, Clone)] pub struct MultiConstraint { @@ -198,7 +199,7 @@ impl MultiConstraint { (constraints, conjunctive) } - pub fn compile(&self, other_operator: i64) -> String { + pub fn compile(&self, other_operator: CmpOp) -> String { let mut parts = Vec::new(); for constraint in &self.constraints { let code = constraint.compile(other_operator); diff --git a/crates/shirabe-semver/src/constraint/simple_constraint.rs b/crates/shirabe-semver/src/constraint/simple_constraint.rs index 5a853824..fa08ce85 100644 --- a/crates/shirabe-semver/src/constraint/simple_constraint.rs +++ b/crates/shirabe-semver/src/constraint/simple_constraint.rs @@ -1,61 +1,33 @@ //! ref: composer/vendor/composer/semver/src/Constraint/Constraint.php use crate::constraint::Bound; -use anyhow::bail; +use shirabe_php_shim::{CmpOp, var_export_str, version_compare}; /// Corresponds to PHP's `Constraint`. #[derive(Debug, Clone)] pub struct SimpleConstraint { - pub(crate) operator: i64, + pub(crate) operator: CmpOp, pub(crate) version: String, pub(crate) pretty_string: Option<String>, } impl SimpleConstraint { - pub const OP_EQ: i64 = 0; - pub const OP_LT: i64 = 1; - pub const OP_LE: i64 = 2; - pub const OP_GT: i64 = 3; - pub const OP_GE: i64 = 4; - pub const OP_NE: i64 = 5; - - pub const STR_OP_EQ: &'static str = "=="; - pub const STR_OP_EQ_ALT: &'static str = "="; - pub const STR_OP_LT: &'static str = "<"; - pub const STR_OP_LE: &'static str = "<="; - pub const STR_OP_GT: &'static str = ">"; - pub const STR_OP_GE: &'static str = ">="; - pub const STR_OP_NE: &'static str = "!="; - pub const STR_OP_NE_ALT: &'static str = "<>"; - - fn trans_op_str(op: &str) -> Option<i64> { + fn trans_op_str(op: &str) -> Option<CmpOp> { match op { - "=" => Some(Self::OP_EQ), - "==" => Some(Self::OP_EQ), - "<" => Some(Self::OP_LT), - "<=" => Some(Self::OP_LE), - ">" => Some(Self::OP_GT), - ">=" => Some(Self::OP_GE), - "<>" => Some(Self::OP_NE), - "!=" => Some(Self::OP_NE), + "=" => Some(CmpOp::Eq), + "==" => Some(CmpOp::Eq), + "<" => Some(CmpOp::Lt), + "<=" => Some(CmpOp::Le), + ">" => Some(CmpOp::Gt), + ">=" => Some(CmpOp::Ge), + "<>" => Some(CmpOp::Ne), + "!=" => Some(CmpOp::Ne), _ => None, } } - fn trans_op_int(op: i64) -> &'static str { - match op { - Self::OP_EQ => "==", - Self::OP_LT => "<", - Self::OP_LE => "<=", - Self::OP_GT => ">", - Self::OP_GE => ">=", - Self::OP_NE => "!=", - _ => panic!("unknown operator: {}", op), - } - } - pub fn new(operator: String, version: String, pretty_string: Option<String>) -> Self { - let op_int = Self::trans_op_str(&operator).unwrap_or_else(|| { + let op = 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. panic!( @@ -66,7 +38,7 @@ impl SimpleConstraint { }); Self { - operator: op_int, + operator: op, version, pretty_string, } @@ -76,77 +48,80 @@ impl SimpleConstraint { &self.version } - pub fn get_operator(&self) -> &'static str { - Self::trans_op_int(self.operator) + pub fn get_operator(&self) -> CmpOp { + self.operator } pub fn get_supported_operators() -> Vec<&'static str> { vec!["=", "==", "<", "<=", ">", ">=", "<>", "!="] } - pub fn get_operator_constant(operator: &str) -> i64 { - Self::trans_op_str(operator).expect("valid operator") + pub fn get_operator_constant(operator: CmpOp) -> i64 { + match operator { + CmpOp::Eq => 0, + CmpOp::Lt => 1, + CmpOp::Le => 2, + CmpOp::Gt => 3, + CmpOp::Ge => 4, + CmpOp::Ne => 5, + } + } + + pub fn from_operator_constant(constant: i64) -> Option<CmpOp> { + Some(match constant { + 0 => CmpOp::Eq, + 1 => CmpOp::Lt, + 2 => CmpOp::Le, + 3 => CmpOp::Gt, + 4 => CmpOp::Ge, + 5 => CmpOp::Ne, + _ => return None, + }) } pub fn version_compare( &self, a: &str, b: &str, - operator: &str, + operator: CmpOp, compare_branches: bool, - ) -> anyhow::Result<bool> { - if Self::trans_op_str(operator).is_none() { - bail!( - "Invalid operator \"{}\" given, expected one of: {}", - operator, - Self::get_supported_operators().join(", ") - ); - } - + ) -> bool { let a_is_branch = a.starts_with("dev-"); let b_is_branch = b.starts_with("dev-"); - if operator == "!=" && (a_is_branch || b_is_branch) { - return Ok(a != b); + if operator == CmpOp::Ne && (a_is_branch || b_is_branch) { + return a != b; } if a_is_branch && b_is_branch { - return Ok(operator == "==" && a == b); + return operator == CmpOp::Eq && a == b; } + // when branches are not comparable, we make sure dev branches never match anything if !compare_branches && (a_is_branch || b_is_branch) { - return Ok(false); + return false; } - Ok(shirabe_php_shim::version_compare(a, b, operator)) + version_compare(a, b, operator) } - pub fn compile_constraint(&self, other_operator: i64) -> String { + pub fn compile_constraint(&self, other_operator: CmpOp) -> String { if self.version.starts_with("dev-") { - if Self::OP_EQ == self.operator { - if Self::OP_EQ == other_operator { - return format!( - "$b && $v === {}", - shirabe_php_shim::var_export_str(&self.version, true) - ); + if CmpOp::Eq == self.operator { + if CmpOp::Eq == other_operator { + return format!("$b && $v === {}", var_export_str(&self.version, true)); } - if Self::OP_NE == other_operator { - return format!( - "!$b || $v !== {}", - shirabe_php_shim::var_export_str(&self.version, true) - ); + if CmpOp::Ne == other_operator { + return format!("!$b || $v !== {}", var_export_str(&self.version, true)); } return "false".to_string(); } - if Self::OP_NE == self.operator { - if Self::OP_EQ == other_operator { - return format!( - "!$b || $v !== {}", - shirabe_php_shim::var_export_str(&self.version, true) - ); + if CmpOp::Ne == self.operator { + if CmpOp::Eq == other_operator { + return format!("!$b || $v !== {}", var_export_str(&self.version, true)); } - if Self::OP_NE == other_operator { + if CmpOp::Ne == other_operator { return "true".to_string(); } return "!$b".to_string(); @@ -155,69 +130,69 @@ impl SimpleConstraint { return "false".to_string(); } - if Self::OP_EQ == self.operator { - if Self::OP_EQ == other_operator { + if CmpOp::Eq == self.operator { + if CmpOp::Eq == other_operator { return format!( "\\version_compare($v, {}, '==')", - shirabe_php_shim::var_export_str(&self.version, true) + var_export_str(&self.version, true) ); } - if Self::OP_NE == other_operator { + if CmpOp::Ne == other_operator { return format!( "$b || \\version_compare($v, {}, '!=')", - shirabe_php_shim::var_export_str(&self.version, true) + var_export_str(&self.version, true) ); } return format!( "!$b && \\version_compare({}, $v, '{}')", - shirabe_php_shim::var_export_str(&self.version, true), - Self::trans_op_int(other_operator) + var_export_str(&self.version, true), + other_operator ); } - if Self::OP_NE == self.operator { - if Self::OP_EQ == other_operator { + if CmpOp::Ne == self.operator { + if CmpOp::Eq == other_operator { return format!( "$b || (!$b && \\version_compare($v, {}, '!='))", - shirabe_php_shim::var_export_str(&self.version, true) + var_export_str(&self.version, true) ); } - if Self::OP_NE == other_operator { + if CmpOp::Ne == other_operator { return "true".to_string(); } return "!$b".to_string(); } - if Self::OP_LT == self.operator || Self::OP_LE == self.operator { - if Self::OP_LT == other_operator || Self::OP_LE == other_operator { + if CmpOp::Lt == self.operator || CmpOp::Le == self.operator { + if CmpOp::Lt == other_operator || CmpOp::Le == other_operator { return "!$b".to_string(); } - } else if Self::OP_GT == other_operator || Self::OP_GE == other_operator { + } else if CmpOp::Gt == other_operator || CmpOp::Ge == other_operator { return "!$b".to_string(); } - if Self::OP_NE == other_operator { + if CmpOp::Ne == other_operator { return "true".to_string(); } let code_comparison = format!( "\\version_compare($v, {}, '{}')", - shirabe_php_shim::var_export_str(&self.version, true), - Self::trans_op_int(self.operator) + var_export_str(&self.version, true), + self.operator ); - if self.operator == Self::OP_LE && other_operator == Self::OP_GT { + if self.operator == CmpOp::Le && other_operator == CmpOp::Gt { return format!( "!$b && \\version_compare($v, {}, '!=') && {}", - shirabe_php_shim::var_export_str(&self.version, true), + var_export_str(&self.version, true), code_comparison ); } - if self.operator == Self::OP_GE && other_operator == Self::OP_LT { + if self.operator == CmpOp::Ge && other_operator == CmpOp::Lt { return format!( "!$b && \\version_compare($v, {}, '!=') && {}", - shirabe_php_shim::var_export_str(&self.version, true), + var_export_str(&self.version, true), code_comparison ); } @@ -226,13 +201,13 @@ impl SimpleConstraint { } 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('=', ""); + let no_equal_op = self.operator.to_string().replace('=', ""); + let provider_no_equal_op = provider.operator.to_string().replace('=', ""); - let is_equal_op = Self::OP_EQ == self.operator; - let is_non_equal_op = Self::OP_NE == self.operator; - let is_provider_equal_op = Self::OP_EQ == provider.operator; - let is_provider_non_equal_op = Self::OP_NE == provider.operator; + let is_equal_op = CmpOp::Eq == self.operator; + let is_non_equal_op = CmpOp::Ne == self.operator; + let is_provider_equal_op = CmpOp::Eq == provider.operator; + let is_provider_non_equal_op = CmpOp::Ne == provider.operator; if is_non_equal_op || is_provider_non_equal_op { if is_non_equal_op @@ -254,12 +229,15 @@ impl SimpleConstraint { if !is_equal_op && !is_provider_equal_op { return true; } - return self - .version_compare(&provider.version, &self.version, "!=", compare_branches) - .expect("valid operator"); + return self.version_compare( + &provider.version, + &self.version, + CmpOp::Ne, + compare_branches, + ); } - if self.operator != Self::OP_EQ && no_equal_op == provider_no_equal_op { + if self.operator != CmpOp::Eq && no_equal_op == provider_no_equal_op { return !(self.version.starts_with("dev-") || provider.version.starts_with("dev-")); } @@ -269,18 +247,10 @@ impl SimpleConstraint { (&provider.version, &self.version, self.operator) }; - if self - .version_compare( - version1, - version2, - Self::trans_op_int(operator), - compare_branches, - ) - .expect("valid operator") - { - return !(Self::trans_op_int(provider.operator) == provider_no_equal_op - && Self::trans_op_int(self.operator) != no_equal_op - && shirabe_php_shim::version_compare(&provider.version, &self.version, "==")); + if self.version_compare(version1, version2, operator, compare_branches) { + return !(provider.operator.to_string() == provider_no_equal_op + && self.operator.to_string() != no_equal_op + && version_compare(&provider.version, &self.version, CmpOp::Eq)); } false @@ -294,26 +264,25 @@ impl SimpleConstraint { } match self.operator { - Self::OP_EQ => ( + CmpOp::Eq => ( Bound::new(self.version.clone(), true), Bound::new(self.version.clone(), true), ), - Self::OP_LT => (Bound::zero(), Bound::new(self.version.clone(), false)), - Self::OP_LE => (Bound::zero(), Bound::new(self.version.clone(), true)), - Self::OP_GT => ( + CmpOp::Lt => (Bound::zero(), Bound::new(self.version.clone(), false)), + CmpOp::Le => (Bound::zero(), Bound::new(self.version.clone(), true)), + CmpOp::Gt => ( Bound::new(self.version.clone(), false), Bound::positive_infinity(), ), - Self::OP_GE => ( + CmpOp::Ge => ( Bound::new(self.version.clone(), true), Bound::positive_infinity(), ), - Self::OP_NE => (Bound::zero(), Bound::positive_infinity()), - _ => panic!("unknown operator: {}", self.operator), + CmpOp::Ne => (Bound::zero(), Bound::positive_infinity()), } } - pub fn compile(&self, other_operator: i64) -> String { + pub fn compile(&self, other_operator: CmpOp) -> String { self.compile_constraint(other_operator) } @@ -337,6 +306,6 @@ impl SimpleConstraint { impl std::fmt::Display for SimpleConstraint { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{} {}", Self::trans_op_int(self.operator), self.version) + write!(f, "{} {}", self.operator, self.version) } } diff --git a/crates/shirabe-semver/src/intervals.rs b/crates/shirabe-semver/src/intervals.rs index 0f9db7cd..9199b13b 100644 --- a/crates/shirabe-semver/src/intervals.rs +++ b/crates/shirabe-semver/src/intervals.rs @@ -7,6 +7,7 @@ use crate::constraint::MultiConstraint; use crate::constraint::SimpleConstraint; use crate::interval::{DevConstraintSet, Interval}; use indexmap::IndexMap; +use shirabe_php_shim::{CmpOp, array_unique, version_compare, version_compare_ordering}; use std::sync::{Mutex, OnceLock}; #[derive(Debug, Clone)] @@ -138,10 +139,10 @@ impl Intervals { // with the start of the current interval and end of next interval, so // [>=M, <N] || [>N, <P] => [>=M, !=N, <P] but M/P can be skipped if they are // zero/+inf - if interval.get_end().get_operator() == "<" && i + 1 < count { + if interval.get_end().get_operator() == CmpOp::Lt && i + 1 < count { let next_interval = &intervals.numeric[i + 1]; if interval.get_end().get_version() == next_interval.get_start().get_version() - && next_interval.get_start().get_operator() == ">" + && next_interval.get_start().get_operator() == CmpOp::Gt { // only add a start if we didn't already do so, can be skipped if we're // looking at second interval in [>=M, <N] || [>N, <P] || [>P, <Q] where @@ -188,8 +189,8 @@ impl Intervals { // convert back >= x - <= x intervals to == x if interval.get_start().get_version() == interval.get_end().get_version() - && interval.get_start().get_operator() == ">=" - && interval.get_end().get_operator() == "<=" + && interval.get_start().get_operator() == CmpOp::Ge + && interval.get_end().get_operator() == CmpOp::Le { constraints.push( SimpleConstraint::new( @@ -416,7 +417,7 @@ impl Intervals { branches }; - branches.names = shirabe_php_shim::array_unique(&branches.names); + branches.names = array_unique(&branches.names); if numeric_groups.len() == 1 { return Ok(IntervalCollection { @@ -443,13 +444,11 @@ impl Intervals { } borders.sort_by(|a, b| { - let order = shirabe_php_shim::version_compare_2(&a.0, &b.0); - if order == 0 { + let order = version_compare_ordering(&a.0, &b.0); + order.then_with(|| { let diff = op_sort_order(&a.1) - op_sort_order(&b.1); diff.cmp(&0) - } else { - order.cmp(&0) - } + }) }); let mut active_intervals: i64 = 0; @@ -477,9 +476,9 @@ impl Intervals { } 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 - if shirabe_php_shim::version_compare(start_c.get_version(), version, "=") - && ((start_c.get_operator() == ">" && operator == "<=") - || (start_c.get_operator() == ">=" && operator == "<")) + if version_compare(start_c.get_version(), version, CmpOp::Eq) + && ((start_c.get_operator() == CmpOp::Gt && operator == "<=") + || (start_c.get_operator() == CmpOp::Ge && operator == "<")) { // skip invalid interval (equivalent to PHP's unset($intervals[$index])) } else { @@ -513,7 +512,7 @@ impl Intervals { // != dev-foo means any numeric version may match, we treat >/< like != they are not // really defined for branches - if op == "!=" { + if op == CmpOp::Ne { intervals.push(Interval::new( Interval::from_zero(), Interval::until_positive_infinity(), @@ -522,7 +521,7 @@ impl Intervals { names: vec![constraint.get_version().to_string()], exclude: true, }; - } else if op == "==" { + } else if op == CmpOp::Eq { branches.names.push(constraint.get_version().to_string()); } @@ -532,7 +531,7 @@ impl Intervals { }); } - if op.starts_with('>') { + if op.to_string().starts_with('>') { // > & >= return Ok(IntervalCollection { numeric: vec![Interval::new( @@ -542,14 +541,14 @@ impl Intervals { branches: Interval::no_dev(), }); } - if op.starts_with('<') { + if op.to_string().starts_with('<') { // < & <= return Ok(IntervalCollection { numeric: vec![Interval::new(Interval::from_zero(), constraint.clone())], branches: Interval::no_dev(), }); } - if op == "!=" { + if op == CmpOp::Ne { // convert !=x to intervals of 0 - <x && >x - +inf + dev* return Ok(IntervalCollection { numeric: vec