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}