// Copyright 2024 The Fuchsia Authors
//
// Licensed under a BSD-style license <LICENSE-BSD>, Apache License, Version 2.0
// <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT
// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option.
// This file may not be copied, modified, or distributed except according to
// those terms.

// Make sure that the derive macros will respect the
// `#[zerocopy(crate = "...")]` attribute when renaming the crate.

// See comment in `include.rs` for why we disable the prelude.
#![no_implicit_prelude]
#![allow(warnings)]

include!("include.rs");

#[test]
fn test_gen_custom_zerocopy() {
    #[derive(
        imp::ByteEq,
        imp::ByteHash,
        imp::IntoBytes,
        imp::FromBytes,
        imp::Unaligned,
        imp::Immutable,
        imp::KnownLayout,
    )]
    #[zerocopy(crate = "fake_zerocopy")]
    #[repr(packed)]
    struct SomeStruct {
        a: u16,
        b: u32,
    }

    impl AssertNotZerocopyIntoBytes for SomeStruct {}
    impl AssertNotZerocopyFromBytes for SomeStruct {}
    impl AssertNotZerocopyUnaligned for SomeStruct {}
    impl AssertNotZerocopyImmutable for SomeStruct {}
    impl AssertNotZerocopyKnownLayout for SomeStruct {}

    fake_zerocopy::assert::<SomeStruct>();
}

mod fake_zerocopy {
    pub use super::imp::*;
    use ::std::{io, ptr::NonNull, unimplemented};

    pub fn assert<T>()
    where
        T: IntoBytes + FromBytes + Unaligned + Immutable,
    {
    }

    pub unsafe trait IntoBytes {
        fn only_derive_is_allowed_to_implement_this_trait()
        where
            Self: Sized;

        fn as_bytes(&self) -> &[u8]
        where
            Self: Immutable,
        {
            unimplemented!()
        }
    }

    pub unsafe trait FromBytes: FromZeros {
        fn only_derive_is_allowed_to_implement_this_trait()
        where
            Self: Sized;
    }

    pub unsafe trait Unaligned {
        fn only_derive_is_allowed_to_implement_this_trait()
        where
            Self: Sized;
    }

    pub unsafe trait Immutable {
        fn only_derive_is_allowed_to_implement_this_trait()
        where
            Self: Sized;
    }

    pub unsafe trait KnownLayout {
        fn only_derive_is_allowed_to_implement_this_trait()
        where
            Self: Sized;

        type PointerMetadata: PointerMetadata;

        type MaybeUninit: ?Sized + KnownLayout<PointerMetadata = Self::PointerMetadata>;

        const LAYOUT: DstLayout;

        fn raw_from_ptr_len(bytes: NonNull<u8>, meta: Self::PointerMetadata) -> NonNull<Self>;

        fn pointer_to_metadata(ptr: *mut Self) -> Self::PointerMetadata;
    }

    macro_rules! impl_ty {
        ($ty:ty $(as $generic:ident)?) => {
            unsafe impl$(<$generic: IntoBytes>)? IntoBytes for $ty {
                fn only_derive_is_allowed_to_implement_this_trait()
                where
                    Self: Sized,
                {
                    unimplemented!()
                }
            }

            unsafe impl$(<$generic: FromBytes>)? FromBytes for $ty {
                fn only_derive_is_allowed_to_implement_this_trait()
                where
                    Self: Sized,
                {
                    unimplemented!()
                }
            }

            unsafe impl$(<$generic: Unaligned>)? Unaligned for $ty {
                fn only_derive_is_allowed_to_implement_this_trait()
                where
                    Self: Sized,
                {
                    unimplemented!()
                }
            }

            unsafe impl$(<$generic: Immutable>)? Immutable for $ty {
                fn only_derive_is_allowed_to_implement_this_trait()
                where
                    Self: Sized,
                {
                    unimplemented!()
                }
            }

            unsafe impl$(<$generic: KnownLayout>)? KnownLayout for $ty {
                fn only_derive_is_allowed_to_implement_this_trait()
                where
                    Self: Sized,
                {
                    unimplemented!()
                }

                type PointerMetadata = ();

                type MaybeUninit = ();

                const LAYOUT: DstLayout = DstLayout::new_zst(None);

                fn raw_from_ptr_len(
                    bytes: NonNull<u8>,
                    meta: Self::PointerMetadata,
                ) -> NonNull<Self> {
                    unimplemented!()
                }

                fn pointer_to_metadata(ptr: *mut Self) -> Self::PointerMetadata {
                    unimplemented!()
                }
            }
        };
    }

    impl_ty!(());
    impl_ty!(u16);
    impl_ty!(u32);
    impl_ty!([T] as T);
    impl_ty!(::std::mem::MaybeUninit<T> as T);
}

pub trait AssertNotZerocopyIntoBytes {}
impl<T: imp::IntoBytes> AssertNotZerocopyIntoBytes for T {}

pub trait AssertNotZerocopyFromBytes {}
impl<T: imp::FromBytes> AssertNotZerocopyFromBytes for T {}

pub trait AssertNotZerocopyUnaligned {}
impl<T: imp::Unaligned> AssertNotZerocopyUnaligned for T {}

pub trait AssertNotZerocopyImmutable {}
impl<T: imp::Immutable> AssertNotZerocopyImmutable for T {}

pub trait AssertNotZerocopyKnownLayout {}
impl<T: imp::KnownLayout> AssertNotZerocopyKnownLayout for T {}
