apt-edsp 0.4.1

A Rust data model of the APT External Dependency Solver Protocol.
Documentation
use std::fmt::Debug;

use serde::{Deserialize, Serialize};

pub struct TestCase<T> {
    repr: &'static str,
    val: T,
}

impl<T> TestCase<T> {
    pub fn new(repr: &'static str, val: T) -> Self {
        Self { repr, val }
    }
}

impl<T: Eq + Serialize + Debug> TestCase<T> {
    pub fn check_ser(&self, serialize_fn: impl Fn(&T) -> String) {
        assert_eq!(
            self.repr,
            (serialize_fn)(&self.val),
            "Incorrect serialized value from '{:?}' (left: expected, right: actual)",
            self.val
        );
    }
}

impl<T: Eq + Deserialize<'static> + Debug> TestCase<T> {
    pub fn check_de(&self, deserialize_fn: impl Fn(&'static str) -> T) {
        assert_eq!(
            self.val,
            (deserialize_fn)(self.repr),
            "Incorrect deserialized value from '{}' (left: expected, right: actual)",
            self.repr
        );
    }
}

pub fn struct_to_string<T: Serialize + Debug>(val: &T) -> String {
    match rfc822_like::to_string(val) {
        Ok(t) => t,
        Err(e) => panic!("Error when serializing {val:?}: {e}"),
    }
}

pub fn struct_from_str<'de, T: Deserialize<'de>>(s: &'de str) -> T {
    match rfc822_like::from_str(s) {
        Ok(t) => t,
        Err(e) => panic!("Error when deserializing \"{s}\": {e}"),
    }
}

pub fn value_to_string<T: Serialize + Debug>(val: &T) -> String {
    #[derive(Serialize, Debug)]
    struct Record<'a, V> {
        xxx: &'a V,
    }

    struct_to_string(&Record { xxx: val }).trim()["xxx: ".len()..].to_string()
}

pub fn value_from_str<T: for<'de> Deserialize<'de>>(s: &str) -> T {
    #[derive(Deserialize)]
    struct Record<V> {
        xxx: V,
    }

    struct_from_str::<Record<T>>(&format!("xxx: {s}")).xxx
}

macro_rules! serde_test {
    ($name:ident: {$($repr:expr => $val:expr),+ $(,)?}) => {
        serde_test!(
            $name(
            crate::test_util::struct_to_string,
            crate::test_util::struct_from_str
            ): {$($repr => $val),+}
        );
    };

    ($name:ident($serialize_fn:expr, $deserialize_fn:expr): {$($repr:expr => $val:expr),+ $(,)?}) => {
        serde_test!(@test
            $name,
            $serialize_fn,
            $deserialize_fn, $($repr, $val)+;
        );
    };

    (@test $name:ident, $serialize_fn:expr, $deserialize_fn:expr, $($repr:expr, $val:expr)+;) => {
        #[test]
        fn $name() {
            $(
            {
                let repr = { $repr };
                let val = { $val };
                let test_case = crate::test_util::TestCase::new(repr, val);
                test_case.check_ser($serialize_fn);
                test_case.check_de($deserialize_fn);
            }
            )+
        }
    };
}

macro_rules! ser_test {
    ($name:ident: {$($repr:expr => $val:expr),+ $(,)?}) => {
        ser_test!(
            $name(crate::test_util::struct_to_string): {$($repr => $val),+}
        );
    };

    ($name:ident($serialize_fn:expr): {$($repr:expr => $val:expr),+ $(,)?}) => {
        ser_test!(@test
            $name,
            $serialize_fn,
            $($repr, $val)+;
        );
    };

    (@test $name:ident, $serialize_fn:expr, $($repr:expr, $val:expr)+;) => {
        #[test]
        fn $name() {
            $(
            {
                let repr = { $repr };
                let val = { $val };
                let test_case = crate::test_util::TestCase::new(repr, val);
                test_case.check_ser($serialize_fn);
            }
            )+
        }
    };
}

pub(crate) use ser_test;
pub(crate) use serde_test;