#[rustfmt::skip]
pub use {
    ::safer_ffi::{
        dyn_traits::DynDrop,
        prelude::{char_p, Out},
    },
};

/// Marker trait that has to be implemented for stuff to be usable in types
/// such as `repr_c::Vec`.
pub trait NonOpaque: Sized {}

/// Same as `c_slice`, but for coming with a `NonOpaque` obligation / safeguard.
pub mod c_slice {
    use super::*;

    guarded_type_alias! {
        pub
        type Box<T> where { T : NonOpaque } = ::safer_ffi::prelude::c_slice::Box<T>;
    }

    guarded_type_alias! {
        pub
        type Ref<'lt, T> where { T : 'lt + NonOpaque } = ::safer_ffi::prelude::c_slice::Ref<'lt, T>;
    }
}

impl NonOpaque for char_p::Box {}
impl NonOpaque for char_p::Ref<'_> {}
impl<T: NonOpaque> NonOpaque for ::safer_ffi::prelude::c_slice::Box<T> {}
impl<T: NonOpaque> NonOpaque for ::safer_ffi::prelude::c_slice::Ref<'_, T> {}
impl NonOpaque for i64 {}
impl NonOpaque for u64 {}
impl NonOpaque for usize {}
impl NonOpaque for u8 {}
impl NonOpaque for bool {}

pub trait HasNiche {}
impl<T: HasNiche> NonOpaque for Option<T> {}

impl HasNiche for char_p::Box {}
impl HasNiche for char_p::Ref<'_> {}
impl<T: NonOpaque> HasNiche for ::safer_ffi::prelude::c_slice::Box<T> {}
impl<T: NonOpaque> HasNiche for ::safer_ffi::prelude::c_slice::Ref<'_, T> {}

// Reduced subset of safer-ffi elements deemed non-error-prone
pub mod repr_c {
    use super::*;

    guarded_type_alias! {
        /// Same as `repr_c::Vec`, but for coming with a `NonOpaque` obligation / safeguard.
        pub
        type Vec<T> where { T : NonOpaque } = ::safer_ffi::prelude::repr_c::Vec<T>;
    }

    /// "Same" as `repr_c::Box`, but for coming with an `impl Drop` obligation.
    #[repr(transparent)]
    pub struct Box<T>
    where
        Self: FfiDrop,
    {
        ptr: ::core::ptr::NonNull<T>,
    }

    pub use self::Box as Box_;

    unsafe impl<T> ::safer_ffi::layout::ReprC for Box<T>
    where
        Self: FfiDrop,
    {
        type CLayout = *mut u8;

        fn is_valid(ptr: &Self::CLayout) -> bool {
            !ptr.is_null()
        }
    }

    impl<T> Drop for Box<T>
    where
        Self: FfiDrop,
    {
        fn drop(&mut self) {
            unsafe {
                FfiDrop::drop(self);
            }
        }
    }

    unsafe impl<T> Send for Box<T>
    where
        Self: FfiDrop,
        ::std::boxed::Box<T>: Send,
    {
    }

    unsafe impl<T> Sync for Box<T>
    where
        Self: FfiDrop,
        ::std::boxed::Box<T>: Sync,
    {
    }

    impl<T> ::core::ops::Deref for Box<T>
    where
        Self: FfiDrop,
    {
        type Target = T;

        fn deref(self: &'_ Self) -> &'_ T {
            unsafe { self.ptr.as_ref() }
        }
    }

    impl<T> ::core::ops::DerefMut for Box<T>
    where
        Self: FfiDrop,
    {
        fn deref_mut(self: &'_ mut Self) -> &'_ mut T {
            unsafe { self.ptr.as_mut() }
        }
    }

    impl<T: ::std::fmt::Debug> ::std::fmt::Debug for Box_<T>
    where
        Self: FfiDrop,
    {
        fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
            T::fmt(self, f)
        }
    }

    /// The drop glue can be written for concrete types.
    impl<T: NonOpaque> FfiDrop for repr_c::Box<T> {
        unsafe fn drop(&mut self) {
            unsafe {
                drop(::std::boxed::Box::from_raw(self.ptr.as_ptr()));
            }
        }
    }

    /// Pointer indirection guarantees concreteness.
    impl<T> NonOpaque for Box<T> where Box<T>: FfiDrop {}

    pub use result::*;
    mod result {
        include!(concat!(env!("CARGO_MANIFEST_DIR"), "/", "result.rs"));
    }
}

pub trait FfiDrop {
    /// # Safety
    ///
    /// Must not be called directly.
    unsafe fn drop(&mut self);
}

#[cfg_attr(all(rustfmt), rustfmt::skip)]
macro_rules! extern_type_polyfill {
    (
        $( #[$attrs:meta] )*
        unsafe extern $($abi:literal)? {
            $($contents:tt)*
        }
    ) => (
        $crate::ffi_utils::extern_type_polyfill! {
            @internal
                [$($attrs)*]
                [$($abi)?]
            $($contents)*
        }
    );

    // extern type case;
    (
        @internal $attrs:tt $abi:tt

        $( #[$ty_attrs:meta] )*
        $pub:vis type $TypeName:ident
            $(:
                $SendOrSync:ident
                $(+ $SendOrSync2:ident)*
                $(+)?
            )?
        ;

        $($rest:tt)*
    ) => (
        $( #[$ty_attrs] )*
        #[repr(C)]
        $pub
        struct $TypeName {
            _private: [u8; 0],
            __not_send_nor_sync: ::core::marker::PhantomData<*const ()>,
        }

        ::paste::paste! {
            $pub type [<Boxed $TypeName>] =
                $crate::ffi_utils::repr_c::Box<$TypeName>
            ;
        }

        $(
            unsafe impl $SendOrSync for $TypeName {}
            $(
                unsafe impl $SendOrSync2 for $TypeName {}
            )*
        )?

        /// Ensure that `$TypeName : NonOpaque` cannot happen.
        const _: () = {
            // Trick: inferring the type param of `Opaque<_>` there becomes
            // ambiguous the moment `$TypeName : Opaque` holds.
            let _: <$TypeName as Opaque<_>>::__;

            trait Opaque<__ : ?Sized> {
                type __;
            }

            impl<T : ?Sized + $crate::ffi_utils::NonOpaque>
                Opaque<[(); 0]>
            for
                T
            {
                type __ = ();
            }

            impl Opaque<[(); 1]> for $TypeName {
                type __ = ();
            }
        };

        $crate::ffi_utils::extern_type_polyfill! {
            @internal $attrs $abi
            $($rest)*
        }
    );

    // Ffi drop case
    (
        @internal $attrs:tt $abi:tt

        #[drop]
        $pub:vis fn $fname:ident (
            $arg_name:tt : $BoxedTy:ty $(,)?
        );

        $($rest:tt)*
    ) => (
        $crate::ffi_utils::extern_type_polyfill! {
            @internal $attrs $abi

            /* `#[drop]` stripped */
            $pub fn $fname (
                $arg_name : $BoxedTy,
            );

            $($rest)*
        }

        impl $crate::ffi_utils::FfiDrop for $BoxedTy {
            unsafe
            fn drop (self: &'_ mut Self)
            {
                unsafe {
                    $fname(<*const _>::read(self))
                }
            }
        }
    );

    // Default case
    (
        @internal [$($attrs:tt)*] [$($abi:literal)?]

        $(#[$fn_attrs:meta])*
        $pub:vis
        $(unsafe $(@$if_unsafe:tt)?)?
        fn $fname:ident $(< $($a:lifetime $(: $b:lifetime)?),* >)? (
            $($arg_name:ident : $ArgTy:ty),* $(,)?
        ) $(-> $Ret:ty)?;

        $($rest:tt)*
    ) => (
        $(#[$fn_attrs])*
        #[inline]
        $pub
        $($($if_unsafe)?
            unsafe
        )?
        fn $fname $(< $($a $(: $b)?),* >)? (
            $($arg_name : $ArgTy),*
        ) $(-> $Ret)?
        {
            $( #[$attrs] )*
            #[allow(improper_ctypes)]
            extern $($abi)? {
                $(#[$fn_attrs])*
                $pub
                fn $fname $(< $($a $(: $b)?),* >)? (
                    $($arg_name : $ArgTy),*
                ) $(-> $Ret)?
                ;
            }
            unsafe {
                $fname($($arg_name),*)
            }
        }

        $crate::ffi_utils::extern_type_polyfill! {
            @internal [$($attrs)*] [$($abi)?]
            $($rest)*
        }
    );

    (
        @internal $attrs:tt $abi:tt
        /* Nothing left to parse */
    ) => (
        /* Done. */
    );
}
pub(crate) use extern_type_polyfill;

#[cfg_attr(all(rustfmt), rustfmt::skip)]
macro_rules! guarded_type_alias {
    (
        $( #[doc = $doc:tt] )*
        $pub:vis
        type $TypeName:ident [$($generics:tt)*]
        where {
            $($where_clauses:tt)*
        } = $T:ty;
    ) => (::paste::paste! {
        #[cfg(not(doc))]
        #[allow(nonstandard_style)]
        $pub
        trait [<__ChecksForTyAlias__$TypeName>] <$($generics)*>
        where
            $($where_clauses)*
        {
            type T : ?Sized;
        }

        #[cfg(not(doc))]
        impl<$($generics)*>
            [<__ChecksForTyAlias__$TypeName>] <$($generics)*>
        for
            ()
        where
            $($where_clauses)*
        {
            type T = $T;
        }

        #[cfg(not(doc))]
        $pub type $TypeName<$($generics)*> =
            <
                ()
                as
                [<__ChecksForTyAlias__$TypeName>] <$($generics)*>
            >::T
        ;

        $( #[doc = $doc] )*
        #[cfg(doc)]
        $pub type $TypeName<$($generics)*> where $($where_clauses)* = $T;

    });

    (
        $( #[doc = $doc:expr] )*
        $pub:vis type $TypeName:ident
        <
            $($lt:lifetime),*
            $(,)?
            $($T:ident),*
        > $($rest:tt)*
    ) => (
        $crate::ffi_utils::guarded_type_alias! {
            $(#[doc = $doc])*
            $pub type $TypeName [$($lt ,)* $($T),*] $($rest)*
        }
    );
}
pub(crate) use guarded_type_alias;

/// Defines a `#[repr(i32)] enum Foo { Bar, Baz }` as a
/// `struct Foo { discriminant: i32 }` with `Foo::Bar, Foo::Baz` as `const`
/// aliases of `Foo { discriminant: Bar as _ }`, _etc._
///
/// This properly models the non-exhaustive nature, **AT THE ABI-LEVEL**, of
/// these FFI C `enum`s.
#[cfg_attr(all(rustfmt), rustfmt::skip)]
macro_rules! non_exhaustive_ffi_enum {(
    #[repr($int:tt)]
    $( #$attr:tt )*
    $pub:vis
    enum $EnumName:ident {
        $(
            $( #[doc $($doc:tt)*] )*
            $Variant:ident $(= $value:expr)?
        ),* $(,)?
    }
) => (
    #[repr(transparent)]
    $( #$attr )*
    $pub
    struct $EnumName {
        discriminant: $int,
    }

    #[allow(warnings, clippy::all)]
    const _: () = {
        #[derive(Debug)]
        #[repr($int)]
        enum Discriminants {
            $(
                $Variant $(= $value)?
            ),*
        }

        impl $EnumName {
            $(
                $( #[doc $($doc)*] )*
                $pub const $Variant: Self = Self {
                    discriminant: Discriminants::$Variant as _,
                };
            )*
        }

        impl ::core::fmt::Debug for $EnumName {
            fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>)
              -> ::core::fmt::Result
            {
                match *self {
                $(
                    | Self::$Variant => Discriminants::$Variant.fmt(f),
                )*
                    | Self { discriminant: unknown_discriminant } => {
                        #[::core::prelude::v1::derive(::core::fmt::Debug)]
                        struct $EnumName {
                            unknown_discriminant: $int,
                        }

                        $EnumName { unknown_discriminant }.fmt(f)
                    }
                }
            }
        }
    };
)}
pub(crate) use non_exhaustive_ffi_enum;
