pkgs-cli 0.2.0

A simple cli tool to manage packages.
Documentation
use std::fmt::{self, Formatter};
use std::marker::PhantomData;

use serde::de::{MapAccess, Visitor};
use serde::{Deserialize, Deserializer};

pub fn deserialize_map_as_vec<'de, D, K, V>(deserializer: D) -> Result<Vec<(K, V)>, D::Error>
where
    D: Deserializer<'de>,
    K: Deserialize<'de>,
    V: Deserialize<'de>,
{
    struct MapAsVecVisitor<K, V> {
        marker: PhantomData<Vec<(K, V)>>,
    }

    impl<'de, K, V> Visitor<'de> for MapAsVecVisitor<K, V>
    where
        K: Deserialize<'de>,
        V: Deserialize<'de>,
    {
        type Value = Vec<(K, V)>;

        fn expecting(&self, formatter: &mut Formatter) -> fmt::Result {
            formatter.write_str("a map to be deserialized as a vector of key-value pairs")
        }

        fn visit_map<M>(self, mut access: M) -> Result<Self::Value, M::Error>
        where
            M: MapAccess<'de>,
        {
            let mut vec = Vec::with_capacity(access.size_hint().unwrap_or(0));
            while let Some((key, value)) = access.next_entry()? {
                vec.push((key, value));
            }
            Ok(vec)
        }
    }

    deserializer.deserialize_map(MapAsVecVisitor {
        marker: PhantomData,
    })
}

#[cfg(test)]
mod tests {
    use indoc::indoc;

    use super::*;
    use crate::test_utils::prelude::*;

    #[derive(Debug, Deserialize)]
    struct My {
        #[serde(deserialize_with = "deserialize_map_as_vec")]
        items: Vec<(String, i32)>,
    }

    #[gtest]
    fn parse_toml() -> Result<()> {
        let toml = indoc! {r#"
            [items]
            a = 1
            d = 4
            g = -1
            b = 2
            c = 3
        "#};

        let my: My = toml::from_str(toml)?;

        expect_eq!(
            my.items,
            [
                ("a".into(), 1),
                ("d".into(), 4),
                ("g".into(), -1),
                ("b".into(), 2),
                ("c".into(), 3),
            ]
        );

        Ok(())
    }

    #[gtest]
    fn parse_yaml() -> Result<()> {
        let yaml = indoc! {r#"
            items:
              a: 1
              d: 4
              g: -1
              b: 2
              c: 3
        "#};

        let my: My = serde_yaml_ng::from_str(yaml)?;

        expect_eq!(
            my.items,
            [
                ("a".into(), 1),
                ("d".into(), 4),
                ("g".into(), -1),
                ("b".into(), 2),
                ("c".into(), 3),
            ]
        );

        Ok(())
    }
}