// Copyright 2024, Offchain Labs, Inc.
// For licensing, see https://github.com/OffchainLabs/stylus-sdk-rs/blob/main/licenses/COPYRIGHT.md

//! This module provides functions for code generated by `stylus-sdk-proc` for the `export-abi`
//! command. Most users shouldn't call these.

use core::any::TypeId;

use alloy_primitives::{Address, Bytes, FixedBytes, Signed, Uint};

/// Represents a unique Solidity Type.
pub struct InnerType {
    /// Full interface string.
    pub name: String,
    /// Unique identifier for de-duplication when printing interfaces.
    pub id: TypeId,
}

/// Trait for collecting structs and error types.
pub trait InnerTypes {
    /// Collect any structs and errors under the type.
    /// Empty for primitives.
    fn inner_types() -> Vec<InnerType> {
        vec![]
    }
}

impl<O, E> InnerTypes for Result<O, E>
where
    O: InnerTypes,
    E: InnerTypes,
{
    fn inner_types() -> Vec<InnerType> {
        let mut out = O::inner_types();
        out.extend(E::inner_types());
        out
    }
}

impl<T: InnerTypes> InnerTypes for Vec<T> {
    fn inner_types() -> Vec<InnerType> {
        T::inner_types()
    }
}

impl<const N: usize, T: InnerTypes> InnerTypes for [T; N] {
    fn inner_types() -> Vec<InnerType> {
        T::inner_types()
    }
}

macro_rules! impl_inner {
    ($ty:ident $($rest:ident)+) => {
        impl_inner!($ty);
        impl_inner!($($rest)+);
    };
    ($ty:ident) => {
        impl InnerTypes for $ty {}
    };
}

impl_inner!(bool u8 u16 u32 u64 u128 i8 i16 i32 i64 i128 String Address Bytes);

impl<const B: usize, const L: usize> InnerTypes for Uint<B, L> {}
impl<const B: usize, const L: usize> InnerTypes for Signed<B, L> {}
impl<const N: usize> InnerTypes for FixedBytes<N> {}

macro_rules! impl_tuple {
    () => {
        impl InnerTypes for () {}
    };
    ($first:ident $(, $rest:ident)*) => {
        impl<$first: InnerTypes $(, $rest: InnerTypes)*> InnerTypes for ( $first $(, $rest)* , ) {
            fn inner_types() -> Vec<InnerType> {
                vec![]
            }
        }

        impl_tuple! { $($rest),* }
    };
}

impl_tuple!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X);
