facti-lib 0.2.1

Facti common library
Documentation
use serde::{de::Visitor, Deserialize, Serialize};

use super::{dependency::Dependency, version::Version, FactorioVersion};

impl Serialize for Version {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: serde::Serializer,
    {
        let str = format!("{}.{}.{}", self.major, self.minor, self.patch);
        serializer.serialize_str(str.as_str())
    }
}

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

        impl<'de> Visitor<'de> for VersionVisitor {
            type Value = Version;

            fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
                formatter.write_str("a valid version string ('major.minor.patch')")
            }

            fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
            where
                E: serde::de::Error,
            {
                Version::parse(v).map_err(|_| serde::de::Error::custom("invalid version"))
            }
        }

        deserializer.deserialize_str(VersionVisitor)
    }
}

impl Serialize for FactorioVersion {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: serde::Serializer,
    {
        let str = if let Some(patch) = self.patch {
            format!("{}.{}.{}", self.major, self.minor, patch)
        } else {
            format!("{}.{}", self.major, self.minor)
        };

        serializer.serialize_str(str.as_str())
    }
}

impl<'de> Deserialize<'de> for FactorioVersion {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: serde::Deserializer<'de>,
    {
        struct FactorioVersionVisitor;

        impl<'de> Visitor<'de> for FactorioVersionVisitor {
            type Value = FactorioVersion;

            fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
                formatter.write_str("a valid Factorio version string ('major.minor')")
            }

            fn visit_none<E>(self) -> Result<Self::Value, E>
            where
                E: serde::de::Error,
            {
                Ok(FactorioVersion::default())
            }

            fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
            where
                E: serde::de::Error,
            {
                FactorioVersion::parse(v)
                    .map_err(|_| serde::de::Error::custom("invalid Factorio version"))
            }
        }

        deserializer.deserialize_str(FactorioVersionVisitor)
    }
}

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

impl<'de> Deserialize<'de> for Dependency {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: serde::Deserializer<'de>,
    {
        struct DependencyVisitor;

        impl<'de> Visitor<'de> for DependencyVisitor {
            type Value = Dependency;

            fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
                formatter.write_str("a valid dependency string ('[mode] name [version-req]')")
            }

            fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
            where
                E: serde::de::Error,
            {
                Dependency::parse(v).map_err(|_| serde::de::Error::custom("invalid dependency"))
            }
        }

        deserializer.deserialize_str(DependencyVisitor)
    }
}

#[cfg(test)]
mod tests {
    use crate::version::VersionReq;

    use super::*;

    #[test]
    fn test_serialize_version() {
        let version = Version::parse("1.2.3").unwrap();
        let serialized = serde_json::to_string(&version).unwrap();
        assert_eq!(serialized, "\"1.2.3\"");
    }

    #[test]
    fn test_deserialize_version() {
        let version = Version::parse("1.2.3").unwrap();
        let deserialized: Version = serde_json::from_str("\"1.2.3\"").unwrap();
        assert_eq!(deserialized, version);
    }

    #[test]
    fn test_serialize_factorio_version() {
        let version = FactorioVersion::parse("1.2").unwrap();
        let serialized = serde_json::to_string(&version).unwrap();
        assert_eq!(serialized, "\"1.2\"");
    }

    #[test]
    fn test_deserialize_factorio_version() {
        let version = FactorioVersion::parse("1.2").unwrap();
        let deserialized: FactorioVersion = serde_json::from_str("\"1.2\"").unwrap();
        assert_eq!(deserialized, version);
    }

    #[test]
    fn test_serialize_dependency() {
        let dependency =
            Dependency::optional("facti", VersionReq::parse(">= 4.2.0").unwrap(), false);
        let serialized = serde_json::to_string(&dependency).unwrap();
        assert_eq!(serialized, "\"? facti >= 4.2.0\"");
    }

    #[test]
    fn test_deserialize_dependency() {
        let dependency = Dependency::optional("facti", VersionReq::parse("< 1.0.0").unwrap(), true);
        let deserialized: Dependency = serde_json::from_str("\"(?) facti < 1.0.0\"").unwrap();
        assert_eq!(deserialized, dependency);
    }
}