dede/
lib.rs

1//! # dede
2//! DErive DEref
3//! 
4//! there were already some macros for deriving `Deref`
5//! but wasn't flexible enough
6//! 
7//! this macro supports structs with generic types and tuple structs
8//! 
9//! ```rust
10//! use dede::*;
11//! 
12//! #[derive(Deref, DerefMut)]
13//! pub struct Foo {
14//! 	#[deref]
15//! 	bar: usize
16//! }
17//! ```
18
19
20
21use quote::{ToTokens, quote};
22use syn::{Data, DeriveInput, Fields, ImplItem, Meta, Token, punctuated::Punctuated, Type};
23use proc_macro2::TokenStream as TokenStream2;
24
25#[proc_macro_derive(Deref, attributes(deref))]
26pub fn derive_deref(item: proc_macro::TokenStream) -> proc_macro::TokenStream {
27	let input = syn::parse::<DeriveInput>(item).expect("`Deref` macro can only be used for struct");
28	if let Data::Struct(data_struct) = input.data {
29		let mut field_option: Option<(TokenStream2, &Type)> = None;
30		match &data_struct.fields {
31			Fields::Named(fields_named) => {
32				'a: for field in fields_named.named.iter() {
33					for attr in field.attrs.iter() {
34						if let Meta::Path(path) =  attr.parse_meta().unwrap() {
35							let last = path.segments.last().unwrap();
36							let ident = &last.ident;
37							if ident.to_string() == "deref" {
38								field_option = Some((field.ident.as_ref().unwrap().to_token_stream(), &field.ty));
39								break 'a;
40							}
41						}
42					}
43				}
44			},
45			Fields::Unnamed(fields_unnamed) => {
46				let mut i = 0;
47				'b: for field in fields_unnamed.unnamed.iter() {
48					for attr in field.attrs.iter() {
49						if let Meta::Path(path) =  attr.parse_meta().unwrap() {
50							let last = path.segments.last().unwrap();
51							let ident = &last.ident;
52							if ident.to_string() == "deref" {
53								field_option = Some((syn::Index::from(i).to_token_stream(), &field.ty));
54								break 'b;
55							}
56						}
57					}
58					i += 1;
59				}
60			},
61			Fields::Unit => {}
62		};
63		if let Some((field_token, field_type)) = field_option {
64			let mut type_args: Punctuated<_, syn::token::Comma> = Punctuated::new();
65			for param in input.generics.type_params() {
66				type_args.push(param.ident.clone());
67			}
68
69			let items = quote! {
70				type Target = #field_type;
71
72				fn deref(&self) -> &Self::Target {
73					&self.#field_token
74				}
75			};
76
77			let span = proc_macro2::Span::call_site();
78			let ident = &input.ident;
79			syn::ItemImpl {
80				attrs: vec![],
81				defaultness: None,
82				unsafety: None,
83				impl_token: Token![impl](span),
84				generics: input.generics.clone(),
85				trait_: Some((None, syn::parse2(quote! { std::ops::Deref }).unwrap(), Token![for](span))),
86				self_ty: Box::new(syn::parse2(quote! { #ident<#type_args> }).unwrap()),
87				brace_token: syn::token::Brace { span },
88				items: vec![ImplItem::Verbatim(items)] 
89			}.to_token_stream().into()
90		} else {
91			proc_macro::TokenStream::new()
92		}
93	} else {
94		proc_macro::TokenStream::new()
95	}
96}
97
98#[proc_macro_derive(DerefMut, attributes(deref))]
99pub fn derive_deref_mut(item: proc_macro::TokenStream) -> proc_macro::TokenStream {
100	let input = syn::parse::<DeriveInput>(item).expect("`DerefMut` macro can only be used for struct");
101	if let Data::Struct(data_struct) = input.data {
102		let mut field_option: Option<(TokenStream2, &Type)> = None;
103		match &data_struct.fields {
104			Fields::Named(fields_named) => {
105				'a: for field in fields_named.named.iter() {
106					for attr in field.attrs.iter() {
107						if let Meta::Path(path) =  attr.parse_meta().unwrap() {
108							let last = path.segments.last().unwrap();
109							let ident = &last.ident;
110							if ident.to_string() == "deref" {
111								field_option = Some((field.ident.as_ref().unwrap().to_token_stream(), &field.ty));
112								break 'a;
113							}
114						}
115					}
116				}
117			},
118			Fields::Unnamed(fields_unnamed) => {
119				let mut i = 0;
120				'b: for field in fields_unnamed.unnamed.iter() {
121					for attr in field.attrs.iter() {
122						if let Meta::Path(path) =  attr.parse_meta().unwrap() {
123							let last = path.segments.last().unwrap();
124							let ident = &last.ident;
125							if ident.to_string() == "deref" {
126								field_option = Some((syn::Index::from(i).to_token_stream(), &field.ty));
127								break 'b;
128							}
129						}
130					}
131					i += 1;
132				}
133			},
134			Fields::Unit => {}
135		};
136		if let Some((field_token, _field_type)) = field_option {
137			let mut type_args: Punctuated<_, syn::token::Comma> = Punctuated::new();
138			for param in input.generics.type_params() {
139				type_args.push(param.ident.clone());
140			}
141
142			let items = quote! {
143				fn deref_mut(&mut self) -> &mut Self::Target {
144					&mut self.#field_token
145				}
146			};
147
148			let span = proc_macro2::Span::call_site();
149			let ident = &input.ident;
150			syn::ItemImpl {
151				attrs: vec![],
152				defaultness: None,
153				unsafety: None,
154				impl_token: Token![impl](span),
155				generics: input.generics.clone(),
156				trait_: Some((None, syn::parse2(quote! { std::ops::DerefMut }).unwrap(), Token![for](span))),
157				self_ty: Box::new(syn::parse2(quote! { #ident<#type_args> }).unwrap()),
158				brace_token: syn::token::Brace { span },
159				items: vec![ImplItem::Verbatim(items)] 
160			}.to_token_stream().into()
161		} else {
162			proc_macro::TokenStream::new()
163		}
164	} else {
165		proc_macro::TokenStream::new()
166	}
167}