projection_macros/
lib.rs

1#![feature(proc_macro_diagnostic)]
2extern crate proc_macro;
3use ::darling::{FromDeriveInput, FromField, FromMeta};
4use ::proc_macro::{Diagnostic, Level, TokenStream};
5use ::proc_macro2::TokenStream as TokenStream2;
6use ::quote::{format_ident, quote};
7use ::syn::{parse_macro_input, AttributeArgs, DeriveInput};
8
9#[derive(Debug, FromMeta)]
10#[darling(default)]
11struct ProjectionArgs {
12	option: bool,
13	result: bool,
14}
15
16#[derive(FromField)]
17#[darling(forward_attrs)]
18struct ProjectionField {
19	ident: Option<::syn::Ident>,
20	vis: ::syn::Visibility,
21	ty: ::syn::Type,
22	//attrs: Vec<::syn::Attribute>,
23}
24
25#[derive(FromDeriveInput)]
26#[darling(supports(struct_named))]
27struct ProjectionStruct {
28	ident: ::syn::Ident,
29	data: ::darling::ast::Data<(), ProjectionField>,
30	vis: ::syn::Visibility,
31}
32
33impl Default for ProjectionArgs {
34	fn default() -> Self {
35		Self {
36			option: true,
37			result: true,
38		}
39	}
40}
41
42fn generate_option_impl(p: &ProjectionStruct, out: &mut TokenStream2) {
43	let orig_name = p.ident.clone();
44	let struct_name_base = format_ident!("__projection_internal_Option_{}_projected", p.ident);
45	let vis = &p.vis;
46	let mut generate = |for_mut, for_ref| {
47		use ::darling::ast::Data::*;
48		let fields = match &p.data {
49			Enum(_) => panic!(),
50			Struct(s) => s,
51		};
52		let struct_name = if for_mut {
53			format_ident!("{}_ref_mut", struct_name_base)
54		} else if for_ref {
55			format_ident!("{}_ref", struct_name_base)
56		} else {
57			struct_name_base.clone()
58		};
59		let mut_token = if for_mut {
60			quote! { mut }
61		} else {
62			quote! {}
63		};
64		let ref_token = if for_ref {
65			quote! { & }
66		} else {
67			quote! {}
68		};
69		let lifetime_token = if for_ref {
70			quote! { 'a }
71		} else {
72			quote! {}
73		};
74		let projected_fields: TokenStream2 = fields
75			.iter()
76			.map(|f| {
77				let ident = f.ident.as_ref().unwrap();
78				let ty = &f.ty;
79				let vis = &f.vis;
80				quote! { #vis #ident : Option<#ref_token #lifetime_token #mut_token #ty>, }
81			})
82			.collect();
83		let projected_fields_init: TokenStream2 = fields
84			.iter()
85			.map(|f| {
86				let ident = f.ident.as_ref().unwrap();
87				quote! { #ident: Some(#ref_token #mut_token f.#ident), }
88			})
89			.collect();
90		out.extend(quote! {
91			#[derive(Default)]
92			#[allow(non_camel_case_types)]
93			#vis struct #struct_name<#lifetime_token> {
94				#projected_fields
95			}
96			impl<#lifetime_token> From<#ref_token #lifetime_token #mut_token #orig_name> for
97			    #struct_name<#lifetime_token> {
98				fn from(f: #ref_token #lifetime_token #mut_token #orig_name) -> Self {
99					Self {
100						#projected_fields_init
101					}
102				}
103			}
104
105			impl<#lifetime_token> ::projection::OptionProjectable for
106			    #ref_token #lifetime_token #mut_token #orig_name {
107				type P = #struct_name<#lifetime_token>;
108				fn project(f: Option<Self>) -> Self::P {
109					match f {
110						Some(t) => t.into(),
111						None => Default::default(),
112					}
113				}
114			}
115		});
116	};
117	generate(false, true);
118	generate(true, true);
119	generate(false, false);
120}
121
122fn generate_result_impl(_p: &ProjectionStruct, _out: &mut TokenStream2) {}
123
124#[proc_macro_attribute]
125pub fn projection(args: TokenStream, input: TokenStream) -> TokenStream {
126	fn imp(attr: AttributeArgs, input: DeriveInput) -> Result<TokenStream, ::darling::Error> {
127		use ::quote::ToTokens;
128		let project = ProjectionStruct::from_derive_input(&input)?;
129		let project_args = ProjectionArgs::from_list(&attr)?;
130		let mut ret = TokenStream::new().into();
131		if project_args.option {
132			generate_option_impl(&project, &mut ret);
133		}
134		if project_args.result {
135			generate_result_impl(&project, &mut ret);
136		}
137		input.to_tokens(&mut ret);
138		Ok(ret.into())
139	}
140	let attr = parse_macro_input!(args as AttributeArgs);
141	let input = parse_macro_input!(input as DeriveInput);
142	match imp(attr, input) {
143		Ok(stream) => stream,
144		Err(e) => {
145			Diagnostic::new(Level::Error, e.to_string()).emit();
146			TokenStream::new()
147		}
148	}
149}