field_projection_internal/
field.rs1use 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 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 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}