Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bevyengine
GitHub Repository: bevyengine/bevy
Path: blob/main/crates/bevy_derive/src/derefs.rs
6598 views
1
use proc_macro::{Span, TokenStream};
2
use quote::quote;
3
use syn::{parse_macro_input, Data, DeriveInput, Field, Index, Member, Type};
4
5
const DEREF: &str = "Deref";
6
const DEREF_MUT: &str = "DerefMut";
7
const DEREF_ATTR: &str = "deref";
8
9
pub fn derive_deref(input: TokenStream) -> TokenStream {
10
let ast = parse_macro_input!(input as DeriveInput);
11
12
let ident = &ast.ident;
13
let (field_member, field_type) = match get_deref_field(&ast, false) {
14
Ok(items) => items,
15
Err(err) => {
16
return err.into_compile_error().into();
17
}
18
};
19
let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl();
20
21
TokenStream::from(quote! {
22
impl #impl_generics ::core::ops::Deref for #ident #ty_generics #where_clause {
23
type Target = #field_type;
24
25
fn deref(&self) -> &Self::Target {
26
&self.#field_member
27
}
28
}
29
})
30
}
31
32
pub fn derive_deref_mut(input: TokenStream) -> TokenStream {
33
let ast = parse_macro_input!(input as DeriveInput);
34
35
let ident = &ast.ident;
36
let (field_member, _) = match get_deref_field(&ast, true) {
37
Ok(items) => items,
38
Err(err) => {
39
return err.into_compile_error().into();
40
}
41
};
42
let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl();
43
44
TokenStream::from(quote! {
45
impl #impl_generics ::core::ops::DerefMut for #ident #ty_generics #where_clause {
46
fn deref_mut(&mut self) -> &mut Self::Target {
47
&mut self.#field_member
48
}
49
}
50
})
51
}
52
53
fn get_deref_field(ast: &DeriveInput, is_mut: bool) -> syn::Result<(Member, &Type)> {
54
let deref_kind = if is_mut { DEREF_MUT } else { DEREF };
55
let deref_attr_str = format!("`#[{DEREF_ATTR}]`");
56
57
match &ast.data {
58
Data::Struct(data_struct) if data_struct.fields.is_empty() => Err(syn::Error::new(
59
Span::call_site().into(),
60
format!("{deref_kind} cannot be derived on field-less structs"),
61
)),
62
Data::Struct(data_struct) if data_struct.fields.len() == 1 => {
63
let field = data_struct.fields.iter().next().unwrap();
64
let member = to_member(field, 0);
65
Ok((member, &field.ty))
66
}
67
Data::Struct(data_struct) => {
68
let mut selected_field: Option<(Member, &Type)> = None;
69
for (index, field) in data_struct.fields.iter().enumerate() {
70
for attr in &field.attrs {
71
if !attr.meta.path().is_ident(DEREF_ATTR) {
72
continue;
73
}
74
75
attr.meta.require_path_only()?;
76
77
if selected_field.is_some() {
78
return Err(syn::Error::new_spanned(
79
attr,
80
format!(
81
"{deref_attr_str} attribute can only be used on a single field"
82
),
83
));
84
}
85
86
let member = to_member(field, index);
87
selected_field = Some((member, &field.ty));
88
}
89
}
90
91
if let Some(selected_field) = selected_field {
92
Ok(selected_field)
93
} else {
94
Err(syn::Error::new(
95
Span::call_site().into(),
96
format!("deriving {deref_kind} on multi-field structs requires one field to have the {deref_attr_str} attribute"),
97
))
98
}
99
}
100
_ => Err(syn::Error::new(
101
Span::call_site().into(),
102
format!("{deref_kind} can only be derived on structs"),
103
)),
104
}
105
}
106
107
fn to_member(field: &Field, index: usize) -> Member {
108
field
109
.ident
110
.as_ref()
111
.map(|name| Member::Named(name.clone()))
112
.unwrap_or_else(|| Member::Unnamed(Index::from(index)))
113
}
114
115