pray-core 1.22.0

Core library for Prayfile, the package manager for the language placed before inference
Documentation
use super::PackageSpec;
use crate::constraint::{normalize_version_constraint, version_satisfies};
use crate::{PrayError, PrayResult};

impl PackageSpec {
    pub const LOCAL_VERSION: &'static str = "local";

    pub fn has_release_version(&self) -> bool {
        !self.version.is_empty() && self.version != Self::LOCAL_VERSION
    }

    pub fn recorded_version(&self) -> &str {
        if self.has_release_version() {
            &self.version
        } else {
            Self::LOCAL_VERSION
        }
    }

    pub fn require_release_version(&self) -> PrayResult<()> {
        if self.has_release_version() {
            Ok(())
        } else {
            Err(PrayError::Manifest(format!(
                "package {} needs a version before it can be packaged",
                self.name
            )))
        }
    }

    pub fn satisfy_constraint(&self, constraint: &str) -> PrayResult<()> {
        if !self.has_release_version() {
            let normalized = normalize_version_constraint(constraint);
            if normalized.is_empty() || normalized == "*" {
                return Ok(());
            }
            return Err(PrayError::Resolution(format!(
                "package {} has no version; add spec.version or omit the constraint",
                self.name
            )));
        }
        if version_satisfies(&self.version, constraint)? {
            Ok(())
        } else {
            Err(PrayError::Resolution(format!(
                "package {} version {} does not satisfy constraint {}",
                self.name, self.version, constraint
            )))
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    fn spec_with_version(version: &str) -> PackageSpec {
        PackageSpec {
            name: "project".to_string(),
            version: version.to_string(),
            ..PackageSpec::default()
        }
    }

    #[test]
    fn omitted_version_records_local_and_matches_star() {
        let spec = spec_with_version("");
        assert_eq!(spec.recorded_version(), "local");
        spec.satisfy_constraint("*").expect("star");
        let error = spec.satisfy_constraint("~> 1.0").expect_err("range");
        assert!(error.to_string().contains("has no version"));
        spec.require_release_version().expect_err("pack");
    }

    #[test]
    fn release_version_uses_semver_constraints() {
        let spec = spec_with_version("1.4.3");
        spec.satisfy_constraint("~> 1.4").expect("matches");
        spec.require_release_version().expect("pack");
    }
}