crate-index 0.1.0

A small library for managing a Cargo crate index
Documentation
use lazy_static::lazy_static;
use regex::Regex;
use semver::{Version, VersionReq};

#[derive(thiserror::Error, Debug)]
pub enum Error {
    #[error("Invalid version (required: {required}, given: {given})")]
    Version {
        required: VersionReq,
        given: Version,
    },

    #[error("Crate name mismatch (expected: {expected}, given: {given}")]
    NameMismatch { expected: String, given: String },

    #[error("Crate name '{name}' is invalid: {reason}")]
    InvalidName { name: String, reason: String },
}

impl Error {
    pub(crate) fn version(current: &Version, given: Version) -> Self {
        let required = VersionReq::parse(&format!("> {}", current)).unwrap();

        debug_assert!(!required.matches(&given));

        Self::Version { required, given }
    }

    pub(crate) fn name_mismatch(expected: impl Into<String>, given: impl Into<String>) -> Self {
        Self::NameMismatch {
            given: given.into(),
            expected: expected.into(),
        }
    }

    pub(crate) fn invalid_name(name: impl Into<String>, reason: impl Into<String>) -> Self {
        Self::InvalidName {
            name: name.into(),
            reason: reason.into(),
        }
    }
}

fn is_allowed_name(name: &str) -> bool {
    let disallowed_names = vec!["nul"];

    !disallowed_names.contains(&name)
}

pub(crate) fn name(name: &str) -> Result<(), Error> {
    lazy_static! {
        static ref REGEX: Regex = Regex::new("^[a-zA-Z][a-zA-Z0-9-_]*$").unwrap();
    }

    if name.is_empty() {
        Err(Error::invalid_name(name, "crate name cannot be empty"))
    } else if !is_allowed_name(name) {
        Err(Error::invalid_name(name, "crate name is blacklisted"))
    } else if !REGEX.is_match(name) || !name.is_ascii() {
        Err(Error::invalid_name(
            name,
            "crate name must be ASCII, be alphanumeric + '-' and '_', and begin with a letter ([a-zA-Z][a-zA-Z0-9-_]*).",
        ))
    } else {
        Ok(())
    }
}