use super::super::Version;
use super::types::{Bound, BoundSet};
use rez_next_common::RezCoreError;
pub(super) fn parse_range_str(s: &str) -> Result<Vec<BoundSet>, RezCoreError> {
if s.is_empty() || s == "*" {
return Ok(vec![BoundSet::any()]);
}
if s == "empty" || s == "!*" {
return Ok(vec![BoundSet::none()]);
}
if s.contains("..") && !s.starts_with('.') {
if let Some(dot_pos) = s.find("..") {
let left = &s[..dot_pos];
let right = &s[dot_pos + 2..];
if !left.is_empty()
&& !right.is_empty()
&& !left.starts_with('>')
&& !left.starts_with('<')
&& !left.starts_with('=')
&& !left.starts_with('!')
&& !left.starts_with('~')
{
let new_s = format!(">={},<{}", left.trim(), right.trim());
return parse_range_str(&new_s);
}
}
}
let or_parts: Vec<&str> = s.split('|').collect();
let mut result = Vec::new();
for or_part in or_parts {
let bound_set = parse_conjunction(or_part.trim())?;
result.push(bound_set);
}
Ok(result)
}
pub(super) fn parse_conjunction(s: &str) -> Result<BoundSet, RezCoreError> {
if s.is_empty() || s == "*" {
return Ok(BoundSet::any());
}
let s = if s.contains('+')
&& !s.starts_with('>')
&& !s.starts_with('<')
&& !s.starts_with('=')
&& !s.starts_with('!')
&& !s.starts_with('~')
{
if let Some(plus_pos) = s.find('+') {
let prefix = &s[..plus_pos];
let suffix = &s[plus_pos + 1..];
if suffix.is_empty() {
format!(">={prefix}")
} else {
format!(">={prefix},{suffix}")
}
} else {
s.to_string()
}
} else {
s.to_string()
};
let mut bounds = Vec::new();
let parts = split_constraint_parts(&s);
for part in parts {
let part = part.trim();
if part.is_empty() {
continue;
}
let bound = parse_single_constraint(part)?;
bounds.push(bound);
}
if bounds.is_empty() {
return Ok(BoundSet::any());
}
Ok(BoundSet { bounds })
}
fn split_constraint_parts(s: &str) -> Vec<String> {
let mut parts = Vec::new();
let mut current = String::new();
for ch in s.chars() {
if ch == ',' {
if !current.trim().is_empty() {
parts.push(current.trim().to_string());
}
current = String::new();
} else {
current.push(ch);
}
}
if !current.trim().is_empty() {
parts.push(current.trim().to_string());
}
let mut final_parts = Vec::new();
for part in parts {
let space_parts = split_on_operator_boundaries(&part);
final_parts.extend(space_parts);
}
final_parts
}
fn split_on_operator_boundaries(s: &str) -> Vec<String> {
let mut parts = Vec::new();
let mut current = String::new();
let chars: Vec<char> = s.chars().collect();
let mut i = 0;
while i < chars.len() {
let ch = chars[i];
if ch == ' ' {
let mut j = i + 1;
while j < chars.len() && chars[j] == ' ' {
j += 1;
}
if j < chars.len() {
let next = chars[j];
if next == '>' || next == '<' || next == '=' || next == '!' || next == '~' {
if !current.trim().is_empty() {
parts.push(current.trim().to_string());
}
current = String::new();
i = j;
continue;
}
}
current.push(ch);
} else {
current.push(ch);
}
i += 1;
}
if !current.trim().is_empty() {
parts.push(current.trim().to_string());
}
parts
}
pub(super) fn parse_single_constraint(s: &str) -> Result<Bound, RezCoreError> {
let s = s.trim();
if s.is_empty() || s == "*" {
return Ok(Bound::Any);
}
if let Some(rest) = s.strip_prefix(">=") {
let v = Version::parse(rest.trim()).map_err(|e| {
RezCoreError::VersionRange(format!("Invalid version in range '{s}': {e}"))
})?;
return Ok(Bound::Ge(v));
}
if let Some(rest) = s.strip_prefix("<=") {
let v = Version::parse(rest.trim()).map_err(|e| {
RezCoreError::VersionRange(format!("Invalid version in range '{s}': {e}"))
})?;
return Ok(Bound::Le(v));
}
if let Some(rest) = s.strip_prefix("==") {
let v = Version::parse(rest.trim()).map_err(|e| {
RezCoreError::VersionRange(format!("Invalid version in range '{s}': {e}"))
})?;
return Ok(Bound::Eq(v));
}
if let Some(rest) = s.strip_prefix("!=") {
let v = Version::parse(rest.trim()).map_err(|e| {
RezCoreError::VersionRange(format!("Invalid version in range '{s}': {e}"))
})?;
return Ok(Bound::Ne(v));
}
if let Some(rest) = s.strip_prefix("~=") {
let v = Version::parse(rest.trim()).map_err(|e| {
RezCoreError::VersionRange(format!("Invalid version in range '{s}': {e}"))
})?;
return Ok(Bound::Compatible(v));
}
if let Some(rest) = s.strip_prefix('>') {
let v = Version::parse(rest.trim()).map_err(|e| {
RezCoreError::VersionRange(format!("Invalid version in range '{s}': {e}"))
})?;
return Ok(Bound::Gt(v));
}
if let Some(rest) = s.strip_prefix('<') {
let v = Version::parse(rest.trim()).map_err(|e| {
RezCoreError::VersionRange(format!("Invalid version in range '{s}': {e}"))
})?;
return Ok(Bound::Lt(v));
}
let v = Version::parse(s).map_err(|e| {
RezCoreError::VersionRange(format!("Invalid version constraint '{s}': {e}"))
})?;
Ok(Bound::Eq(v))
}