trait_guard/
lib.rs

1//! # trait_guard
2//! 
3//! `trait_guard` is a macro used to protect a trait implementation from usage with a custom message, note, and label.
4//! 
5//! It abuses the `on_unimplemented` attribute to provide a custom error message when the trait is not implemented. It requires
6//! the `negative_impls` and `trivial_bounds` nightly features to be enabled.
7//! 
8//! You can use any trait (even if it's an STD trait or from another crate)!
9//! 
10//! ## Example usage:
11//! 
12//! ```rust
13//! trait_guard!(
14//!     MyType, // the type that will be guarded
15//!     MyTrait, // the trait that is being guarded
16//! 
17//!     trait_guard = MyGuardTrait, // optional: custom name for the guard trait, shown in the diagnostic message
18//!     guard_struct = MyGuardStruct, // optional: custom name for the guard struct, shown in the diagnostic message
19//! 
20//!     {
21//!         // this is the body of the trait implementation
22//!         fn my_method(&self) {
23//!             // implementation
24//!         }
25//!     },
26//! 
27//!     message = "MyType does not implement MyTrait",
28//! //  note = "This is a custom note",
29//! //  label = "MyType needs to implement MyTrait"
30//! );
31//! ```
32//! 
33//! The example above will refuse to compile if the `MyTrait` implementation of `MyType` is used anywhere in the user's code. 
34//! 
35//! If you want to guard multiple traits/types, you can change the name of `trait_guard` and `guard_struct` to avoid conflicts.
36//! 
37//! ## Example for [`std::fmt::Display`]
38//! 
39//! ```rust
40//! use trait_guard::trait_guard;
41//! 
42//! #[derive(Debug)]
43//! struct A;
44//! 
45//! trait_guard!(
46//!     A, // the type that will be guarded
47//!     std::fmt::Display, // the trait that is being guarded
48//! 
49//!     {
50//!        // this is the body of the trait implementation
51//!        fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
52//!            unreachable!("It will never be called because A does not implement Display");
53//!        }
54//!     },
55//! 
56//!     message = "A does not implement std::fmt::Display",
57//! );
58//! ```
59
60#[macro_export]
61/// A macro to protect a trait implementation from usage with a custom message, note, and label.
62/// 
63/// This abuses the `on_unimplemented` attribute to provide a custom error message when the trait is not implemented. It requires
64/// the `negative_impls` and `trivial_bounds` nightly features to be enabled.
65/// 
66/// You can use any trait (even if it's an STD trait or from another crate)!
67/// 
68/// # Example usage:
69/// 
70/// ```rust
71/// trait_guard!(
72///     MyType, // the type that will be guarded
73///     MyTrait, // the trait that is being guarded
74/// 
75///     trait_guard = MyGuardTrait, // optional: custom name for the guard trait, shown in the diagnostic message
76///     guard_struct = MyGuardStruct, // optional: custom name for the guard struct, shown in the diagnostic message
77/// 
78///     {
79///         // this is the body of the trait implementation
80///         fn my_method(&self) {
81///             // implementation
82///         }
83///     },
84/// 
85///     message = "MyType does not implement MyTrait",
86/// //  note = "This is a custom note",
87/// //  label = "MyType needs to implement MyTrait"
88/// );
89/// ```
90/// 
91/// The example above will refuse to compile if the `MyTrait` implementation of `MyType` is used anywhere in the user's code. 
92/// 
93/// If you want to guard multiple traits/types, you can change the name of `trait_guard` and `guard_struct` to avoid conflicts.
94macro_rules! trait_guard {
95    (
96        $item:ty, 
97        $trait:ty, 
98        $body:tt, 
99        
100        message = $message:expr
101        $(, note = $note:expr)?
102        $(, label = $label:expr)?
103    ) => {
104        #[diagnostic::on_unimplemented(message = $message$(, note = $note)?$(, label = $label)?)]
105        trait GuardTrait {}
106
107        struct Guard<T>(T);
108        impl !GuardTrait for $item {}
109        impl $trait for $item where Guard<$item>: GuardTrait $body
110    };
111
112    (
113        $item:ty, 
114        $trait:ty, 
115        
116        trait_guard = $trait_guard:ident, 
117        guard_struct = $guard_struct:ident, 
118        
119        $body:tt, 
120        
121        message = $message:expr
122        $(, note = $note:expr)?
123        $(, label = $label:expr)?
124    ) => {
125        #[diagnostic::on_unimplemented(message = $message$(, note = $note)?$(, label = $label)?)]
126        trait $trait_guard {}
127
128        struct $guard_struct<T>(T);
129        impl !$trait_guard for $item {}
130        impl $trait for $item where $guard_struct<$item>: $trait_guard $body
131    }
132}