//! SpecShakingMarker is an internal trait used to ensure that contract type specs
//! are included in the WASM binary when types are used at contract boundaries.
//!
//! The trait is called at three external boundary points:
//! 1. Contract function input parameters
//! 2. Contract function return values
//! 3. Event publishing
//!
//! Types that are only used internally (storage, cross-contract calls) do not
//! need their specs included, so the trait is NOT called from general TryFromVal
//! conversions.
//!
//! The trait has a default no-op implementation. Types generated by contracttype,
//! contractevent, and contracterror macros implement this trait to include their
//! spec XDR in the WASM's contractspecv0 section.
//!
//! Types whose layout includes their inner types inline (`Option<T>`,
//! `Result<T, E>`, tuples) call `T::spec_shaking_marker()` directly for
//! each inner type.
//!
//! Types whose size is independent of their inner types (`Vec<T>`,
//! `Map<K, V>`, `&T`, `&mut T`) use [`keep_reachable`] to reference each
//! inner type's marker without calling it. Recursive definitions are
//! possible through these types, so a direct call would risk a runtime cycle.

/// Trait for types that may include their spec in the WASM binary.
///
/// This trait is used internally by the SDK to ensure contract type specs
/// are included when types are used at external boundaries (function params,
/// return values, events).
///
/// Types with `#[contracttype]`, `#[contractevent]`, or `#[contracterror]`
/// will have implementations that include their spec. All other types have
/// a no-op implementation.
#[doc(hidden)]
pub trait SpecShakingMarker {
    /// Include this type's spec in the WASM binary.
    ///
    /// For primitive types and built-in SDK types, this is a no-op.
    /// For user-defined contract types, this ensures the spec XDR is
    /// included in the contractspecv0 section.
    #[inline(always)]
    fn spec_shaking_marker() {}
}

/// Keeps `f`'s symbol alive through the linker's dead-code-elimination
/// pass without invoking `f` at runtime. Takes `f` as a function pointer
/// and volatile-reads it. The same technique is used to keep the `MARKER`
/// byte statics alive (see `soroban-sdk-macros/src/shaking.rs`).
#[doc(hidden)]
#[inline(always)]
fn keep_reachable(_f: fn()) {
    #[cfg(target_family = "wasm")]
    let _ = unsafe { core::ptr::read_volatile(&_f) };
}

// Primitive type implementations (no-op)
impl SpecShakingMarker for () {}
impl SpecShakingMarker for bool {}
impl SpecShakingMarker for u32 {}
impl SpecShakingMarker for i32 {}
impl SpecShakingMarker for u64 {}
impl SpecShakingMarker for i64 {}
impl SpecShakingMarker for u128 {}
impl SpecShakingMarker for i128 {}

// Reference implementations — use `keep_reachable` because &T is
// pointer-sized independent of T, so recursive definitions through
// references could be possible. Currently, limitations of generics
// within `map_type` prevent this from working, so this is done
// out of caution.
impl<T: SpecShakingMarker> SpecShakingMarker for &T {
    #[inline(always)]
    fn spec_shaking_marker() {
        keep_reachable(T::spec_shaking_marker);
    }
}

impl<T: SpecShakingMarker> SpecShakingMarker for &mut T {
    #[inline(always)]
    fn spec_shaking_marker() {
        keep_reachable(T::spec_shaking_marker);
    }
}

// Option implementation - includes inner type's spec
impl<T: SpecShakingMarker> SpecShakingMarker for Option<T> {
    #[inline(always)]
    fn spec_shaking_marker() {
        T::spec_shaking_marker();
    }
}

// Result implementation - includes both types' specs
impl<T: SpecShakingMarker, E: SpecShakingMarker> SpecShakingMarker for Result<T, E> {
    #[inline(always)]
    fn spec_shaking_marker() {
        T::spec_shaking_marker();
        E::spec_shaking_marker();
    }
}

// Tuple implementations
impl<T0: SpecShakingMarker> SpecShakingMarker for (T0,) {
    #[inline(always)]
    fn spec_shaking_marker() {
        T0::spec_shaking_marker();
    }
}

impl<T0: SpecShakingMarker, T1: SpecShakingMarker> SpecShakingMarker for (T0, T1) {
    #[inline(always)]
    fn spec_shaking_marker() {
        T0::spec_shaking_marker();
        T1::spec_shaking_marker();
    }
}

impl<T0: SpecShakingMarker, T1: SpecShakingMarker, T2: SpecShakingMarker> SpecShakingMarker
    for (T0, T1, T2)
{
    #[inline(always)]
    fn spec_shaking_marker() {
        T0::spec_shaking_marker();
        T1::spec_shaking_marker();
        T2::spec_shaking_marker();
    }
}

impl<
        T0: SpecShakingMarker,
        T1: SpecShakingMarker,
        T2: SpecShakingMarker,
        T3: SpecShakingMarker,
    > SpecShakingMarker for (T0, T1, T2, T3)
{
    #[inline(always)]
    fn spec_shaking_marker() {
        T0::spec_shaking_marker();
        T1::spec_shaking_marker();
        T2::spec_shaking_marker();
        T3::spec_shaking_marker();
    }
}

impl<
        T0: SpecShakingMarker,
        T1: SpecShakingMarker,
        T2: SpecShakingMarker,
        T3: SpecShakingMarker,
        T4: SpecShakingMarker,
    > SpecShakingMarker for (T0, T1, T2, T3, T4)
{
    #[inline(always)]
    fn spec_shaking_marker() {
        T0::spec_shaking_marker();
        T1::spec_shaking_marker();
        T2::spec_shaking_marker();
        T3::spec_shaking_marker();
        T4::spec_shaking_marker();
    }
}

impl<
        T0: SpecShakingMarker,
        T1: SpecShakingMarker,
        T2: SpecShakingMarker,
        T3: SpecShakingMarker,
        T4: SpecShakingMarker,
        T5: SpecShakingMarker,
    > SpecShakingMarker for (T0, T1, T2, T3, T4, T5)
{
    #[inline(always)]
    fn spec_shaking_marker() {
        T0::spec_shaking_marker();
        T1::spec_shaking_marker();
        T2::spec_shaking_marker();
        T3::spec_shaking_marker();
        T4::spec_shaking_marker();
        T5::spec_shaking_marker();
    }
}

impl<
        T0: SpecShakingMarker,
        T1: SpecShakingMarker,
        T2: SpecShakingMarker,
        T3: SpecShakingMarker,
        T4: SpecShakingMarker,
        T5: SpecShakingMarker,
        T6: SpecShakingMarker,
    > SpecShakingMarker for (T0, T1, T2, T3, T4, T5, T6)
{
    #[inline(always)]
    fn spec_shaking_marker() {
        T0::spec_shaking_marker();
        T1::spec_shaking_marker();
        T2::spec_shaking_marker();
        T3::spec_shaking_marker();
        T4::spec_shaking_marker();
        T5::spec_shaking_marker();
        T6::spec_shaking_marker();
    }
}

impl<
        T0: SpecShakingMarker,
        T1: SpecShakingMarker,
        T2: SpecShakingMarker,
        T3: SpecShakingMarker,
        T4: SpecShakingMarker,
        T5: SpecShakingMarker,
        T6: SpecShakingMarker,
        T7: SpecShakingMarker,
    > SpecShakingMarker for (T0, T1, T2, T3, T4, T5, T6, T7)
{
    #[inline(always)]
    fn spec_shaking_marker() {
        T0::spec_shaking_marker();
        T1::spec_shaking_marker();
        T2::spec_shaking_marker();
        T3::spec_shaking_marker();
        T4::spec_shaking_marker();
        T5::spec_shaking_marker();
        T6::spec_shaking_marker();
        T7::spec_shaking_marker();
    }
}

impl<
        T0: SpecShakingMarker,
        T1: SpecShakingMarker,
        T2: SpecShakingMarker,
        T3: SpecShakingMarker,
        T4: SpecShakingMarker,
        T5: SpecShakingMarker,
        T6: SpecShakingMarker,
        T7: SpecShakingMarker,
        T8: SpecShakingMarker,
    > SpecShakingMarker for (T0, T1, T2, T3, T4, T5, T6, T7, T8)
{
    #[inline(always)]
    fn spec_shaking_marker() {
        T0::spec_shaking_marker();
        T1::spec_shaking_marker();
        T2::spec_shaking_marker();
        T3::spec_shaking_marker();
        T4::spec_shaking_marker();
        T5::spec_shaking_marker();
        T6::spec_shaking_marker();
        T7::spec_shaking_marker();
        T8::spec_shaking_marker();
    }
}

impl<
        T0: SpecShakingMarker,
        T1: SpecShakingMarker,
        T2: SpecShakingMarker,
        T3: SpecShakingMarker,
        T4: SpecShakingMarker,
        T5: SpecShakingMarker,
        T6: SpecShakingMarker,
        T7: SpecShakingMarker,
        T8: SpecShakingMarker,
        T9: SpecShakingMarker,
    > SpecShakingMarker for (T0, T1, T2, T3, T4, T5, T6, T7, T8, T9)
{
    #[inline(always)]
    fn spec_shaking_marker() {
        T0::spec_shaking_marker();
        T1::spec_shaking_marker();
        T2::spec_shaking_marker();
        T3::spec_shaking_marker();
        T4::spec_shaking_marker();
        T5::spec_shaking_marker();
        T6::spec_shaking_marker();
        T7::spec_shaking_marker();
        T8::spec_shaking_marker();
        T9::spec_shaking_marker();
    }
}

impl<
        T0: SpecShakingMarker,
        T1: SpecShakingMarker,
        T2: SpecShakingMarker,
        T3: SpecShakingMarker,
        T4: SpecShakingMarker,
        T5: SpecShakingMarker,
        T6: SpecShakingMarker,
        T7: SpecShakingMarker,
        T8: SpecShakingMarker,
        T9: SpecShakingMarker,
        T10: SpecShakingMarker,
    > SpecShakingMarker for (T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10)
{
    #[inline(always)]
    fn spec_shaking_marker() {
        T0::spec_shaking_marker();
        T1::spec_shaking_marker();
        T2::spec_shaking_marker();
        T3::spec_shaking_marker();
        T4::spec_shaking_marker();
        T5::spec_shaking_marker();
        T6::spec_shaking_marker();
        T7::spec_shaking_marker();
        T8::spec_shaking_marker();
        T9::spec_shaking_marker();
        T10::spec_shaking_marker();
    }
}

impl<
        T0: SpecShakingMarker,
        T1: SpecShakingMarker,
        T2: SpecShakingMarker,
        T3: SpecShakingMarker,
        T4: SpecShakingMarker,
        T5: SpecShakingMarker,
        T6: SpecShakingMarker,
        T7: SpecShakingMarker,
        T8: SpecShakingMarker,
        T9: SpecShakingMarker,
        T10: SpecShakingMarker,
        T11: SpecShakingMarker,
    > SpecShakingMarker for (T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11)
{
    #[inline(always)]
    fn spec_shaking_marker() {
        T0::spec_shaking_marker();
        T1::spec_shaking_marker();
        T2::spec_shaking_marker();
        T3::spec_shaking_marker();
        T4::spec_shaking_marker();
        T5::spec_shaking_marker();
        T6::spec_shaking_marker();
        T7::spec_shaking_marker();
        T8::spec_shaking_marker();
        T9::spec_shaking_marker();
        T10::spec_shaking_marker();
        T11::spec_shaking_marker();
    }
}

impl<
        T0: SpecShakingMarker,
        T1: SpecShakingMarker,
        T2: SpecShakingMarker,
        T3: SpecShakingMarker,
        T4: SpecShakingMarker,
        T5: SpecShakingMarker,
        T6: SpecShakingMarker,
        T7: SpecShakingMarker,
        T8: SpecShakingMarker,
        T9: SpecShakingMarker,
        T10: SpecShakingMarker,
        T11: SpecShakingMarker,
        T12: SpecShakingMarker,
    > SpecShakingMarker for (T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12)
{
    #[inline(always)]
    fn spec_shaking_marker() {
        T0::spec_shaking_marker();
        T1::spec_shaking_marker();
        T2::spec_shaking_marker();
        T3::spec_shaking_marker();
        T4::spec_shaking_marker();
        T5::spec_shaking_marker();
        T6::spec_shaking_marker();
        T7::spec_shaking_marker();
        T8::spec_shaking_marker();
        T9::spec_shaking_marker();
        T10::spec_shaking_marker();
        T11::spec_shaking_marker();
        T12::spec_shaking_marker();
    }
}

// SDK type implementations (no-op for built-in types, propagate for containers)
// These are imported via crate:: to avoid circular dependencies

impl SpecShakingMarker for crate::Address {}
impl SpecShakingMarker for crate::Bytes {}
impl<const N: usize> SpecShakingMarker for crate::BytesN<N> {}
impl SpecShakingMarker for crate::String {}
impl SpecShakingMarker for crate::Symbol {}
impl SpecShakingMarker for crate::U256 {}
impl SpecShakingMarker for crate::I256 {}
impl SpecShakingMarker for crate::Timepoint {}
impl SpecShakingMarker for crate::Duration {}
impl SpecShakingMarker for crate::Val {}
impl SpecShakingMarker for crate::Error {}

// SDK Container types - Vec and Map use `keep_reachable` to allow for recursive definitions.
impl<T: SpecShakingMarker> SpecShakingMarker for crate::Vec<T> {
    #[inline(always)]
    fn spec_shaking_marker() {
        keep_reachable(T::spec_shaking_marker);
    }
}

impl<K: SpecShakingMarker, V: SpecShakingMarker> SpecShakingMarker for crate::Map<K, V> {
    #[inline(always)]
    fn spec_shaking_marker() {
        keep_reachable(K::spec_shaking_marker);
        keep_reachable(V::spec_shaking_marker);
    }
}

// Additional SDK types
impl SpecShakingMarker for crate::MuxedAddress {}
impl<const N: usize> SpecShakingMarker for crate::crypto::Hash<N> {}
impl SpecShakingMarker for crate::crypto::bls12_381::Bls12381G1Affine {}
impl SpecShakingMarker for crate::crypto::bls12_381::Bls12381G2Affine {}
impl SpecShakingMarker for crate::crypto::bls12_381::Bls12381Fp {}
impl SpecShakingMarker for crate::crypto::bls12_381::Bls12381Fp2 {}
impl SpecShakingMarker for crate::crypto::bls12_381::Bls12381Fr {}
impl SpecShakingMarker for crate::crypto::bn254::Bn254G1Affine {}
impl SpecShakingMarker for crate::crypto::bn254::Bn254G2Affine {}
impl SpecShakingMarker for crate::crypto::bn254::Bn254Fp {}
impl SpecShakingMarker for crate::crypto::bn254::Bn254Fr {}

// Auth types - these have export=false but are legitimately used at external
// boundaries (as inputs to __check_auth in custom account contracts).
// They don't emit specs themselves because they're internal SDK types.
impl SpecShakingMarker for crate::auth::Context {}
impl SpecShakingMarker for crate::auth::ContractContext {}
impl SpecShakingMarker for crate::auth::CreateContractHostFnContext {}
impl SpecShakingMarker for crate::auth::CreateContractWithConstructorHostFnContext {}
impl SpecShakingMarker for crate::auth::ContractExecutable {}
impl SpecShakingMarker for crate::auth::InvokerContractAuthEntry {}
impl SpecShakingMarker for crate::auth::SubContractInvocation {}
