versian 0.1.0

Parse and compare Debian package versions.
Documentation
use crate::error::DebianVersionError;

pub trait ValidateUpstreamVersion {
    fn validate_with_revision(&self) -> Result<bool, DebianVersionError>;
    fn validate_without_revision(&self) -> Result<bool, DebianVersionError>;
}

impl<T> ValidateUpstreamVersion for T
where
    T: AsRef<str>,
{
    fn validate_with_revision(&self) -> Result<bool, DebianVersionError> {
        let s = self.as_ref();

        if s.is_empty() {
            return Err(DebianVersionError::EmptyUpstream);
        }

        if !s.starts_with(|c| char::is_ascii_digit(&c)) {
            return Err(DebianVersionError::UpstreamStartWithDigit);
        }

        if s.chars().all(|c| {
            char::is_ascii_alphanumeric(&c)
                || ['.', '+', '-', ':', '~'].iter().any(|valid| *valid == c)
        }) {
            return Ok(true);
        } else {
            Err(DebianVersionError::UpstreamInvalidCharacters)
        }
    }

    fn validate_without_revision(&self) -> Result<bool, DebianVersionError> {
        let s = self.as_ref();

        if s.is_empty() {
            return Err(DebianVersionError::EmptyUpstream);
        }

        if !s.starts_with(|c| char::is_ascii_digit(&c)) {
            return Err(DebianVersionError::UpstreamStartWithDigit);
        }

        if only_valid_chars(s) {
            return Ok(true);
        } else {
            Err(DebianVersionError::UpstreamInvalidCharacters)
        }
    }
}

pub trait ValidateDebianRevision {
    fn validate(&self) -> Result<bool, DebianVersionError>;
}

impl<T> ValidateDebianRevision for T
where
    T: AsRef<str>,
{
    fn validate(&self) -> Result<bool, DebianVersionError> {
        let s = self.as_ref();
        if s.is_empty() {
            return Err(DebianVersionError::EmptyRevision);
        }
        if !only_valid_chars(s) {
            return Err(DebianVersionError::RevisionInvalidCharacters);
        }

        Ok(true)
    }
}

pub(crate) fn only_valid_chars(s: &str) -> bool {
    s.chars()
        .all(|c| char::is_ascii_alphanumeric(&c) || c == '+' || c == '.' || c == '~' || c == ':')
}