@@ -10,7 +10,8 @@ use syn::{
10
10
parse:: { Parse , ParseStream } ,
11
11
parse_macro_input, parse_quote,
12
12
spanned:: Spanned ,
13
- DeriveInput , Error , FnArg , Generics , Ident , ItemFn , ItemType , LitStr , Pat , Visibility ,
13
+ DeriveInput , Error , Fields , FnArg , Generics , Ident , ItemFn , ItemStruct , ItemType , LitStr , Pat ,
14
+ Visibility ,
14
15
} ;
15
16
16
17
/// Parses a type definition, extracts its identifier and generic parameters
@@ -80,6 +81,84 @@ pub fn unsafe_guid(args: TokenStream, input: TokenStream) -> TokenStream {
80
81
result. into ( )
81
82
}
82
83
84
+ /// Attribute macro for marking structs as UEFI protocols.
85
+ ///
86
+ /// The macro takes one argument, a GUID string.
87
+ ///
88
+ /// The macro can only be applied to a struct, and the struct must have
89
+ /// named fields (i.e. not a unit or tuple struct). It implements the
90
+ /// [`Protocol`] trait and the `unsafe` [`Identify`] trait for the
91
+ /// struct. It also adds a hidden field that causes the struct to be
92
+ /// marked as [`!Send` and `!Sync`][send-and-sync].
93
+ ///
94
+ /// # Safety
95
+ ///
96
+ /// The caller must ensure that the correct GUID is attached to the
97
+ /// type. An incorrect GUID could lead to invalid casts and other
98
+ /// unsound behavior.
99
+ ///
100
+ /// # Example
101
+ ///
102
+ /// ```
103
+ /// use uefi::{Identify, guid};
104
+ /// use uefi::proto::unsafe_protocol;
105
+ ///
106
+ /// #[unsafe_protocol("12345678-9abc-def0-1234-56789abcdef0")]
107
+ /// struct ExampleProtocol {}
108
+ ///
109
+ /// assert_eq!(ExampleProtocol::GUID, guid!("12345678-9abc-def0-1234-56789abcdef0"));
110
+ /// ```
111
+ ///
112
+ /// [`Identify`]: https://docs.rs/uefi/latest/uefi/trait.Identify.html
113
+ /// [`Protocol`]: https://docs.rs/uefi/latest/uefi/proto/trait.Protocol.html
114
+ /// [send-and-sync]: https://doc.rust-lang.org/nomicon/send-and-sync.html
115
+ #[ proc_macro_attribute]
116
+ pub fn unsafe_protocol ( args : TokenStream , input : TokenStream ) -> TokenStream {
117
+ // Parse `args` as a GUID string.
118
+ let ( time_low, time_mid, time_high_and_version, clock_seq_and_variant, node) =
119
+ match parse_guid ( parse_macro_input ! ( args as LitStr ) ) {
120
+ Ok ( data) => data,
121
+ Err ( tokens) => return tokens. into ( ) ,
122
+ } ;
123
+
124
+ let item_struct = parse_macro_input ! ( input as ItemStruct ) ;
125
+
126
+ let ident = & item_struct. ident ;
127
+ let struct_attrs = & item_struct. attrs ;
128
+ let struct_vis = & item_struct. vis ;
129
+ let struct_fields = if let Fields :: Named ( struct_fields) = & item_struct. fields {
130
+ & struct_fields. named
131
+ } else {
132
+ return err ! ( item_struct, "Protocol struct must used named fields" ) . into ( ) ;
133
+ } ;
134
+ let struct_generics = & item_struct. generics ;
135
+ let ( impl_generics, ty_generics, where_clause) = item_struct. generics . split_for_impl ( ) ;
136
+
137
+ quote ! {
138
+ #( #struct_attrs) *
139
+ #struct_vis struct #ident #struct_generics {
140
+ // Add a hidden field with `PhantomData` of a raw
141
+ // pointer. This has the implicit side effect of making the
142
+ // struct !Send and !Sync.
143
+ _no_send_or_sync: :: core:: marker:: PhantomData <* const u8 >,
144
+ #struct_fields
145
+ }
146
+
147
+ unsafe impl #impl_generics :: uefi:: Identify for #ident #ty_generics #where_clause {
148
+ const GUID : :: uefi:: Guid = :: uefi:: Guid :: from_values(
149
+ #time_low,
150
+ #time_mid,
151
+ #time_high_and_version,
152
+ #clock_seq_and_variant,
153
+ #node,
154
+ ) ;
155
+ }
156
+
157
+ impl #impl_generics :: uefi:: proto:: Protocol for #ident #ty_generics #where_clause { }
158
+ }
159
+ . into ( )
160
+ }
161
+
83
162
/// Create a `Guid` at compile time.
84
163
///
85
164
/// # Example
0 commit comments