#![ doc = include_str!( "../README.md")]

pub use paste;

#[macro_export]
macro_rules! stable_type {
    (
        $(#[$meta:meta])*
        $vis:vis struct $Name:ident [
            $($v_str:literal : { $($f_vis:vis $f_name:ident : $f_ty:ty),* $(,)? }),+ $(,)?
        ]
    ) => {
        $crate::stable_type!(@find_latest
            args: { $(#[$meta])* $vis $Name [ $($v_str : { $($f_vis $f_name : $f_ty),* }),+ ] },
            list: [ $($v_str),+ ]
        );
    };

    (@find_latest args: { $($args:tt)* }, list: [ $head:tt, $($tail:tt),+ ]) => {
        $crate::stable_type!(@find_latest args: { $($args)* }, list: [ $($tail),+ ]);
    };

    (@find_latest args: {
        $(#[$meta:meta])* $vis:vis $Name:ident [ $($v_str:literal : { $($f_vis:vis $f_name:ident : $f_ty:ty),* }),+ ]
    }, list: [ $last_str:literal ]) => {
        $crate::paste::paste! {
            // 1. Generate versioned structs
            $(
                #[derive(serde::Serialize, serde::Deserialize, Debug)]
                #[serde(deny_unknown_fields)]
                struct [<V $v_str:lower>] { $($f_vis $f_name : $f_ty),* }
            )+

            // 2. Generate Versions Enum
            #[derive(serde::Serialize, serde::Deserialize, Debug)]
            #[serde(tag = "version")]
            enum Versions {
                $(
                    #[serde(rename = $v_str)]
                    [<V $v_str:lower>]([<V $v_str:lower>])
                ),+
            }

            // 3. Generate struct based on last entry
            $crate::stable_type!(@gen_struct $(#[$meta])* $vis $Name [ $($v_str : { $($f_vis $f_name : $f_ty),* }),+ ]);

            // 4. Generate conversion chain
            $crate::stable_type!(@gen_chain $Name, [ $($v_str),+ ]);

            // 5. Generate stable wrapper
            #[derive(serde::Serialize, serde::Deserialize, Debug)]
            #[serde(transparent)]
            pub struct Stable(Versions);

            impl From<Versions> for $Name {
                fn from(v: Versions) -> Self {
                    match v {
                        $( Versions::[<V $v_str:lower>](inner) => $Name::from(inner) ),+
                    }
                }
            }

            impl From<Stable> for $Name {
                fn from(stable: Stable) -> Self { stable.0.into() }
            }

            impl From<$Name> for Stable {
                fn from(value: $Name) -> Self {
                    Self(Versions::[<V $last_str:lower>](value.into()))
                }
            }
        }
    };

    (@gen_chain $Name:ident, [ $curr:tt, $next:tt $(, $rest:tt)* ]) => {
        $crate::paste::paste! {
            impl From<[<V $curr:lower>]> for $Name {
                fn from(v: [<V $curr:lower>]) -> Self {
                    let next_v: [<V $next:lower>] = v.into();
                    $Name::from(next_v)
                }
            }
        }
        $crate::stable_type!(@gen_chain $Name, [ $next $(, $rest)* ]);
    };
    (@gen_chain $Name:ident, [ $last:tt ]) => {};

    (@gen_struct $(#[$meta:meta])* $vis:vis $Name:ident [ $v:tt : { $($f_vis:vis $f_name:ident : $f_ty:ty),* } ]) => {
        $(#[$meta])*
        $vis struct $Name { $($f_vis $f_name : $f_ty),* }

        $crate::paste::paste! {
            impl From<[<V $v:lower>]> for $Name {
                fn from([<V $v:lower>] { $($f_name),* }: [<V $v:lower>]) -> Self {
                    Self { $($f_name),* }
                }
            }
            impl From<$Name> for [<V $v:lower>] {
                fn from($Name { $($f_name),* }: $Name) -> Self {
                    Self { $($f_name),* }
                }
            }
        }
    };
    (@gen_struct $(#[$meta:meta])* $vis:vis $Name:ident [ $v:tt : { $($f_vis:vis $f_name:ident : $f_ty:ty),* }, $($rest:tt : { $($rf_vis:vis $rf_name:ident : $rt:ty),* }),+ ]) => {
        $crate::stable_type!(@gen_struct $(#[$meta])* $vis $Name [ $($rest : { $($rf_vis $rf_name : $rt),* }),+ ]);
    };
}
