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 }
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}