chara_card 0.3.0

A library to deal with character card formats and `.charx` in Rust.
Documentation
use serdev::{Deserialize, Deserializer, Serialize, Serializer};
use std::fmt::Display;
use std::str::FromStr;

use crate::raw::Error;

/// Represents a version identifier following the `<major>.<minor>` format.
///
/// Note that there is no patch number by the specification.
///
/// It supports full comparison operators,
/// allowing you to easily sort versions or check for compatibility ranges.
/// Bigger number is bigger.
#[derive(Debug, Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash)]
pub struct Version {
    /// The major version component.
    pub major: u8,
    /// The minor version component.
    pub minor: u8,
}

impl Version {
    /// Represents version of CCv1.
    pub const V1: Self = Self { major: 1, minor: 0 };

    /// Represents version of CCv2.
    pub const V2: Self = Self { major: 2, minor: 0 };

    /// Represents version of CCv3.
    pub const V3: Self = Self { major: 3, minor: 0 };

    /// Gets name of specification corresponding to specification version.
    ///
    /// Note that it returns "chara_card_v1" for consistency for CCv1.
    pub fn name(&self) -> Option<&'static str> {
        match self.major {
            1 => Some("chara_card_v1"),
            2 => Some("chara_card_v2"),
            3 => Some("chara_card_v3"),
            _ => None,
        }
    }
}

impl From<(u8, u8)> for Version {
    fn from((major, minor): (u8, u8)) -> Self {
        Self { major, minor }
    }
}

impl FromStr for Version {
    type Err = Error;

    fn from_str(value: &str) -> Result<Self, Self::Err> {
        let at = value.find('.').ok_or(Error::MalformedVersion)?;

        let (major, minor) = value.split_at(at);
        let major = major.parse().map_err(|_| Error::MalformedVersion)?;
        let minor = minor[1..].parse().map_err(|_| Error::MalformedVersion)?;

        Ok(Self { major, minor })
    }
}

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

impl Serialize for Version {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: Serializer,
    {
        serializer.serialize_str(&self.to_string())
    }
}

impl<'de> Deserialize<'de> for Version {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: Deserializer<'de>,
    {
        use serdev::de::Error;

        let s = String::deserialize(deserializer)?;
        Self::from_str(&s).map_err(D::Error::custom)
    }
}