pub trait AsCopy<T> {
    #[allow(clippy::wrong_self_convention)]
    fn as_copy(self) -> T;
}

impl<T: Copy> AsCopy<T> for T {
    fn as_copy(self) -> T {
        self
    }
}

impl<T: Copy + AsCopy<T>> AsCopy<T> for &T {
    fn as_copy(self) -> T {
        (*self).as_copy()
    }
}

#[cfg(test)]
mod tests {
    use std::{
        fmt::Debug,
        net::{IpAddr, Ipv4Addr},
    };

    use super::*;

    #[derive(Debug, PartialEq, Default)]
    struct MyStruct(usize);

    impl Clone for MyStruct {
        #[allow(clippy::non_canonical_clone_impl)]
        fn clone(&self) -> Self {
            Self(self.0 + 1)
        }
    }

    impl Copy for MyStruct {}

    #[test]
    fn as_copy_should_call_copy_on_a_reference() {
        let source = MyStruct::default();
        let copied = source;

        fn takes_impl<T>(arg: impl AsCopy<T>) -> T {
            arg.as_copy()
        }

        assert_eq!(
            copied,
            takes_impl(
                #[allow(clippy::needless_borrows_for_generic_args)]
                &source
            )
        );
    }

    #[test]
    fn as_copy_should_return_an_owned_value_unchanged() {
        let source = MyStruct::default();
        let equal = MyStruct::default();

        fn takes_impl<T>(arg: impl AsCopy<T>) -> T {
            arg.as_copy()
        }

        assert_eq!(equal, takes_impl(source));
    }

    #[test]
    fn as_copy_should_work_with_any_copyable_type() {
        fn case(value: impl Copy + Debug + PartialEq) {
            assert_eq!(value, value.as_copy());
        }

        case("Hello, world!");
        case(123);
        case(IpAddr::V4(Ipv4Addr::new(192, 168, 0, 1)));
    }
}
