upstream-rs 0.5.5

Fetch package updates directly from the source.
use std::fmt;

use anyhow::{Result, bail};
use serde::{Deserialize, Serialize};

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct Version {
    pub major: u32,
    pub minor: u32,
    pub patch: u32,
    pub is_prerelease: bool,
}

// TODO: implement?
#[derive(Debug, Clone)]
pub enum VersionParseError {
    Empty,
    InvalidFormat(String),
    InvalidMajor(String),
    InvalidMinor(String),
    InvalidPatch(String),
}

impl std::error::Error for VersionParseError {}

impl std::fmt::Display for VersionParseError {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            VersionParseError::Empty => write!(f, "Version string is empty"),
            VersionParseError::InvalidFormat(s) => write!(f, "Invalid version format: {}", s),
            VersionParseError::InvalidMajor(s) => write!(f, "Invalid major version: {}", s),
            VersionParseError::InvalidMinor(s) => write!(f, "Invalid minor version: {}", s),
            VersionParseError::InvalidPatch(s) => write!(f, "Invalid patch version: {}", s),
        }
    }
}

impl Version {
    pub fn new(major: u32, minor: u32, patch: u32, is_prerelease: bool) -> Self {
        Self {
            major,
            minor,
            patch,
            is_prerelease,
        }
    }

    pub fn is_newer_than(&self, other: &Version) -> bool {
        if self.major != other.major {
            return self.major > other.major;
        }
        if self.minor != other.minor {
            return self.minor > other.minor;
        }
        if self.patch != other.patch {
            return self.patch > other.patch;
        }
        if self.is_prerelease != other.is_prerelease {
            return !self.is_prerelease;
        }

        false
    }

    pub fn parse(s: &str) -> Result<Self> {
        if s.trim().is_empty() {
            bail!("Cannot parse empty version",);
        }

        let parts: Vec<&str> = s.split('.').collect();

        if parts.is_empty() || parts.len() > 3 {
            bail!("Invalid version format",);
        }

        let major = match parts[0].parse::<u32>() {
            Ok(v) => v,
            Err(_) => bail!("Invalid major"),
        };

        let minor = if parts.len() > 1 {
            match parts[1].parse::<u32>() {
                Ok(v) => v,
                Err(_) => bail!("Invalid minor"),
            }
        } else {
            0
        };

        let patch = if parts.len() > 2 {
            match parts[2].parse::<u32>() {
                Ok(v) => v,
                Err(_) => bail!("Invalid patch"),
            }
        } else {
            0
        };

        Ok(Version::new(major, minor, patch, false))
    }
}

impl fmt::Display for Version {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        if self.is_prerelease {
            write!(f, "{}.{}.{}-pre", self.major, self.minor, self.patch)
        } else {
            write!(f, "{}.{}.{}", self.major, self.minor, self.patch)
        }
    }
}