Repository: astral-sh/uv
File: crates/uv-pep440/src/version_specifier.rs (Rust)

Code before:
        start += separator.len();
    }
    Ok(version_ranges)
}

/// A `~=` version specifier with a minor version, but no patch version (e.g. `~=3.11`).
#[derive(Clone, Debug)]
pub struct TildeMinorVersionSpecifier<'a> {
    inner: &'a VersionSpecifier,
}

impl<'a> TildeMinorVersionSpecifier<'a> {
    /// Create a new [`TildeMinorVersionSpecifier`] from a [`VersionSpecifier`].
    ///
    /// If a [`Operator::TildeEqual`] is not used, or the version includes more than a minor
    /// segment, this will return [`None`].
    pub fn from_specifier(
        specifier: &'a VersionSpecifier,
    ) -> Option<TildeMinorVersionSpecifier<'a>> {
        if specifier.operator != Operator::TildeEqual {
            return None;
        }
        if specifier.version().release().len() != 2 {
            return None;
        }
        if specifier.version().any_prerelease() {
            return None;
        }
        Some(Self { inner: specifier })
    }

    /// Construct the lower and upper bounding version specifiers for this tilde version specifier,
    /// e.g., for `~=3.13` this would return `>=3.13` and `<4`.
    pub fn bounding_specifiers(&self) -> (VersionSpecifier, VersionSpecifier) {
        let release = self.inner.version().release();
        let lower = self.inner.version.clone();
        let upper = Version::new([release[0] + 1]);
        (
            VersionSpecifier::greater_than_equal_version(lower),
            VersionSpecifier::less_than_version(upper),
        )
    }
}

impl std::fmt::Display for TildeMinorVersionSpecifier<'_> {
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        write!(f, "{}", self.inner)
    }
}

#[cfg(test)]

Review comment (bug):
@zanieb could you briefly explain why this is needed? I'm guessing it is to handle edge cases. If so, should we also check for post and local version?

Code after:
    }
}

/// The specific kind of error that occurs when parsing a single version
/// specifier from a string.
#[derive(Clone, Debug, Eq, PartialEq)]
enum ParseErrorKind {
    InvalidOperator(OperatorParseError),
    InvalidVersion(VersionPatternParseError),
    InvalidSpecifier(VersionSpecifierBuildError),
    MissingOperator(VersionOperatorBuildError),
    MissingVersion,
    InvalidTrailing(String),
}

impl From<ParseErrorKind> for VersionSpecifierParseError {
    fn from(kind: ParseErrorKind) -> Self {
        Self {
            kind: Box::new(kind),
        }
    }
}

/// Parse a list of specifiers such as `>= 1.0, != 1.3.*, < 2.0`.
pub(crate) fn parse_version_specifiers(
    spec: &str,
) -> Result<Vec<VersionSpecifier>, VersionSpecifiersParseError> {
    let mut version_ranges = Vec::new();
    if spec.is_empty() {
        return Ok(version_ranges);
    }
    let mut start: usize = 0;
    let separator = ",";
    for version_range_spec in spec.split(separator) {
        match VersionSpecifier::from_str(version_range_spec) {
            Err(err) => {
                return Err(VersionSpecifiersParseError {
                    inner: Box::new(VersionSpecifiersParseErrorInner {
                        err,
                        line: spec.to_string(),
                        start,
                        end: start + version_range_spec.len(),
                    }),
                });
            }
            Ok(version_range) => {
                version_ranges.push(version_range);
            }
        }
        start += version_range_spec.len();
        start += separator.len();