defuse_near_utils/
panic_on_clone.rs

1use impl_tools::autoimpl;
2use near_sdk::{env, near};
3
4/// This struct is used as a tool to make it possible to derive borsh
5/// serialization in such a way where serialization can take over a reference
6/// and include it as if it's owned by the struct/enum being serialized.
7/// The reference can be taken in the function [`PanicOnClone::from_ref()`].
8#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
9#[autoimpl(Deref using self.0)]
10#[autoimpl(DerefMut using self.0)]
11#[autoimpl(AsRef using self.0)]
12#[autoimpl(AsMut using self.0)]
13#[near(serializers = [borsh])]
14#[repr(transparent)] // needed for `transmute()` below
15pub struct PanicOnClone<T: ?Sized>(T);
16
17impl<T> PanicOnClone<T> {
18    #[inline]
19    pub const fn new(value: T) -> Self {
20        Self(value)
21    }
22
23    #[inline]
24    pub const fn from_ref(value: &T) -> &Self {
25        // this is safe due to `#[repr(transparent)]`
26        unsafe { ::core::mem::transmute::<&T, &Self>(value) }
27    }
28
29    #[inline]
30    pub fn into_inner(self) -> T {
31        self.0
32    }
33}
34
35impl<T> From<T> for PanicOnClone<T> {
36    fn from(value: T) -> Self {
37        Self::new(value)
38    }
39}
40
41impl<T> Clone for PanicOnClone<T> {
42    #[track_caller]
43    fn clone(&self) -> Self {
44        env::panic_str("PanicOnClone")
45    }
46}
47
48#[cfg(test)]
49mod tests {
50    use super::*;
51
52    use core::ptr;
53
54    #[test]
55    fn from_ref() {
56        let value = "example".to_string();
57        let poc = PanicOnClone::from_ref(&value);
58        assert!(ptr::eq(&**poc, &value));
59        assert_eq!(&**poc, &value);
60    }
61
62    #[test]
63    #[should_panic(expected = "PanicOnClone")]
64    #[allow(clippy::redundant_clone)]
65    fn panics_on_clone() {
66        struct NotClonable;
67
68        let _ = PanicOnClone::new(
69            // doesn't implement Clone
70            NotClonable,
71        )
72        .clone();
73    }
74}