field_projection_internal/
field.rs

1use const_fnv1a_hash::fnv1a_hash_str_64 as field_name_hash;
2use proc_macro2::{Literal, Span, TokenStream};
3use quote::{quote, quote_spanned, ToTokens};
4use syn::{
5    punctuated::Punctuated, Data, DeriveInput, Error, Fields, GenericParam, Generics, Member,
6    Result,
7};
8
9pub fn field(input: TokenStream) -> Result<TokenStream> {
10    let DeriveInput {
11        ident,
12        generics,
13        data,
14        ..
15    } = syn::parse2(input)?;
16
17    // Check this is a struct, and extract inner.
18    let data = match data {
19        Data::Struct(v) => v,
20        Data::Enum(v) => {
21            return Err(Error::new(
22                v.enum_token.span,
23                "#[derive(Field)] cannot be applied to enum",
24            ))
25        }
26        Data::Union(v) => {
27            return Err(Error::new(
28                v.union_token.span,
29                "#[derive(Field)] cannot be applied to union",
30            ))
31        }
32    };
33
34    let fields = match data.fields {
35        Fields::Named(v) => v.named,
36        Fields::Unnamed(v) => v.unnamed,
37        Fields::Unit => Punctuated::new(),
38    };
39
40    let field_name: Vec<_> = fields
41        .iter()
42        .enumerate()
43        .map(|(i, field)| match &field.ident {
44            Some(v) => Member::Named(v.clone()),
45            None => Member::Unnamed(i.into()),
46        })
47        .collect();
48
49    // Extract generics and where clauses
50    let Generics {
51        params: generics,
52        where_clause,
53        ..
54    } = generics;
55    let generics: Vec<_> = generics
56        .into_iter()
57        .map(|mut x| {
58            match &mut x {
59                GenericParam::Lifetime(_) => (),
60                GenericParam::Type(t) => t.default = None,
61                GenericParam::Const(c) => c.default = None,
62            }
63            x
64        })
65        .collect();
66    let ty_generics: Vec<_> = generics
67        .iter()
68        .map(|x| -> &dyn ToTokens {
69            match x {
70                GenericParam::Lifetime(l) => &l.lifetime,
71                GenericParam::Type(t) => &t.ident,
72                GenericParam::Const(c) => &c.ident,
73            }
74        })
75        .collect();
76
77    let mixed_site = Span::mixed_site();
78    let mut builder = Vec::new();
79
80    for i in 0..field_name.len() {
81        let field_name_current = &field_name[i];
82        let field_name_str = match field_name_current {
83            Member::Named(v) => v.to_string(),
84            Member::Unnamed(v) => v.index.to_string(),
85        };
86        let ty = &fields[i].ty;
87        let field_name_hash = field_name_hash(&field_name_str);
88        let field_name_literal = Literal::string(&field_name_str);
89
90        builder.push(quote_spanned! {mixed_site =>
91            unsafe impl<
92                #(#generics,)*
93            > field_projection::Field<
94                #ident<#(#ty_generics,)*>
95            > for FieldName<#field_name_hash> #where_clause
96            {
97                type Type = #ty;
98                const NAME: &'static str = #field_name_literal;
99
100                unsafe fn map(ptr: *const #ident<#(#ty_generics,)*>) -> *const Self::Type {
101                    unsafe { core::ptr::addr_of!((*ptr).#field_name_current) }
102                }
103            }
104        })
105    }
106
107    let gen = quote!(#(#builder)*);
108    Ok(gen)
109}