@@ -10,7 +10,8 @@ use syn::{
1010 parse:: { Parse , ParseStream } ,
1111 parse_macro_input, parse_quote,
1212 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 ,
1415} ;
1516
1617/// Parses a type definition, extracts its identifier and generic parameters
@@ -80,6 +81,84 @@ pub fn unsafe_guid(args: TokenStream, input: TokenStream) -> TokenStream {
8081 result. into ( )
8182}
8283
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+
83162/// Create a `Guid` at compile time.
84163///
85164/// # Example
0 commit comments