meta_tuple 0.5.0

A statically typed opaque tuple that can contain any type.
Documentation
use std::any::Any;

use meta_tuple::{MetaAny, MetaItem, MetaTuple, impl_meta_any, meta_tuple, meta_tuple_type};

pub struct Attack;
pub struct Attacker;
pub struct Defender;

#[derive(MetaItem)]
pub struct DamageDealt(usize);

impl Attack {
    pub fn calculate_damage(&self, _: &Attacker, _: &Defender) -> usize {
        40
    }
}

pub trait CardComponent {
    fn play(&self, input: impl MetaTuple) -> impl MetaTuple;

    fn join(self, other: impl CardComponent) -> impl CardComponent
    where
        Self: Sized,
    {
        Join(self, other)
    }
}

pub struct Join<A, B>(A, B);

impl<A: CardComponent, B: CardComponent> CardComponent for Join<A, B> {
    fn play(&self, input: impl MetaTuple) -> impl MetaTuple {
        let out = self.0.play(input);
        self.1.play(out)
    }
}

impl CardComponent for Attack {
    fn play(&self, input: impl MetaTuple) -> impl MetaTuple {
        let attacker = input.get::<Attacker>().unwrap();
        let defender = input.get::<Defender>().unwrap();
        let damage_dealt = self.calculate_damage(attacker, defender);
        input.join(DamageDealt(damage_dealt))
    }
}

pub trait CardComponent2 {
    type Out: MetaTuple;
    fn play<T: MetaTuple>(&self, input: &T) -> Self::Out;

    fn join(self, other: impl CardComponent2) -> impl CardComponent2
    where
        Self: Sized,
    {
        Join(self, other)
    }
}

impl<A: CardComponent2, B: CardComponent2> CardComponent2 for Join<A, B> {
    type Out = meta_tuple_type!(#A::Out, #B::Out);
    fn play<T: MetaTuple>(&self, input: &T) -> Self::Out {
        let out = self.0.play(input);
        let out_2 = self.1.play(&input.join_tuple(&out));
        meta_tuple!(#out, #out_2)
    }
}

pub trait Metadata: MetaAny + Any {}

impl<T> Metadata for T where T: MetaAny + Any {}

impl_meta_any!(Metadata);

#[test]
fn metadata() {
    let boxed: Box<dyn Metadata> = Box::new(());
    assert_eq!(boxed.get::<f32>(), None)
}