rust_release_channel 0.3.0

A data structure for Rust release channel metadata.
Documentation
//! A place to stick our custom serialization implementations
//! so they don't clog up the main code.
use chrono;
use collections;
use serde;
use url_serde;

impl serde::Serialize for super::Channel {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: serde::Serializer,
    {
        #[derive(Serialize)]
        struct EncodedChannel<'a> {
            #[serde(rename = "manifest-version")]
            manifest_version: &'a str,
            date: &'a chrono::naive::NaiveDate,
            #[serde(
                skip_serializing_if = "::std::collections::BTreeMap::is_empty"
            )]
            pkg: &'a collections::BTreeMap<String, super::Package>,
            #[serde(
                skip_serializing_if = "::std::collections::BTreeMap::is_empty"
            )]
            renames: collections::BTreeMap<
                &'a str,
                collections::BTreeMap<&'a str, &'a str>,
            >,
        }

        let mut fake_renames = collections::BTreeMap::new();
        for (old_pkg, new_pkg) in self.renames.iter() {
            let mut inner = collections::BTreeMap::new();
            inner.insert("to", new_pkg.as_str());
            fake_renames.insert(old_pkg.as_str(), inner);
        }

        let output = EncodedChannel {
            manifest_version: "2",
            date: &self.date,
            pkg: &self.pkg,
            renames: fake_renames,
        };

        output.serialize(serializer)
    }
}

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

        #[derive(Deserialize)]
        #[serde(tag = "manifest-version")]
        enum EncodedChannel {
            #[serde(rename = "2")]
            VersionTwo {
                date: chrono::naive::NaiveDate,
                #[serde(default)]
                pkg: collections::BTreeMap<String, super::Package>,
                #[serde(default)]
                renames: collections::BTreeMap<
                    String,
                    collections::BTreeMap<String, String>,
                >,
            },
        }

        match EncodedChannel::deserialize(deserializer)? {
            EncodedChannel::VersionTwo { date, pkg, renames } => {
                let mut fixed_renames = collections::BTreeMap::new();
                for (old_pkg, mut inner_map) in renames {
                    let new_pkg = inner_map
                        .remove("to")
                        .ok_or(D::Error::missing_field("to"))?;
                    fixed_renames.insert(old_pkg, new_pkg);
                }

                Ok(super::Channel {
                    date: date,
                    pkg: pkg,
                    renames: fixed_renames,
                })
            }
        }
    }
}

impl serde::Serialize for super::Artefact {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: serde::Serializer,
    {
        fn slice_is_empty<T>(s: &[T]) -> bool {
            s.is_empty()
        }

        #[derive(Serialize, Default, Debug)]
        struct EncodedArtefact<'a> {
            available: bool,
            url: Option<url_serde::SerdeUrl>,
            hash: Option<&'a str>,
            xz_url: Option<url_serde::SerdeUrl>,
            xz_hash: Option<&'a str>,
            #[serde(skip_serializing_if = "slice_is_empty")]
            components: &'a [super::ArtefactToken],
            #[serde(skip_serializing_if = "slice_is_empty")]
            extensions: &'a [super::ArtefactToken],
        }

        let mut output = EncodedArtefact::default();

        output.available = !self.standalone.is_empty();

        self.standalone
            .get(&super::ArchiveFormat::TarGzip)
            .map(|src| {
                output.url = Some(url_serde::Serde(src.url.clone()));
                output.hash = Some(&src.hash);
            });
        self.standalone
            .get(&super::ArchiveFormat::TarXz)
            .map(|src| {
                output.xz_url = Some(url_serde::Serde(src.url.clone()));
                output.xz_hash = Some(&src.hash);
            });

        output.components = &self.components;
        output.extensions = &self.extensions;

        output.serialize(serializer)
    }
}

impl<'de> serde::Deserialize<'de> for super::Artefact {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: serde::Deserializer<'de>,
    {
        #[derive(Deserialize, Debug)]
        struct EncodedArtefact {
            available: bool,
            url: Option<url_serde::SerdeUrl>,
            hash: Option<String>,
            xz_url: Option<url_serde::SerdeUrl>,
            xz_hash: Option<String>,
            #[serde(default)]
            components: Vec<super::ArtefactToken>,
            #[serde(default)]
            extensions: Vec<super::ArtefactToken>,
        }

        let input = EncodedArtefact::deserialize(deserializer)?;

        let mut output = super::Artefact::default();

        if let (Some(url), Some(hash)) = (input.url, input.hash) {
            output.standalone.insert(
                super::ArchiveFormat::TarGzip,
                super::ArchiveSource::new(url.into_inner(), hash),
            );
        }

        if let (Some(url), Some(hash)) = (input.xz_url, input.xz_hash) {
            output.standalone.insert(
                super::ArchiveFormat::TarXz,
                super::ArchiveSource::new(url.into_inner(), hash),
            );
        }

        output.components = input.components;
        output.extensions = input.extensions;

        Ok(output)
    }
}