derivit_core/
getter.rs

1use darling::FromMeta;
2use quote::{quote, ToTokens};
3
4#[derive(Default, FromMeta, Clone, Copy)]
5pub enum Style {
6  #[darling(rename = "ref")]
7  Ref,
8  #[darling(rename = "move")]
9  #[default]
10  Move,
11}
12
13impl ToTokens for Style {
14  fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
15    match self {
16      Style::Ref => tokens.extend(quote! { & }),
17      Style::Move => tokens.extend(quote! {}),
18    }
19  }
20}
21
22#[derive(Default, FromMeta, Clone)]
23pub struct FieldConverter {
24  pub style: Option<Style>,
25  #[darling(rename = "fn")]
26  pub func: Option<syn::Path>,
27}
28
29#[derive(Default, FromMeta)]
30pub struct FieldGetterOptions {
31  pub rename: Option<syn::Ident>,
32  pub style: Option<Style>,
33  pub attrs: Option<super::Attributes>,
34  #[darling(default, rename = "skip")]
35  pub ignore: bool,
36  #[darling(default, rename = "const")]
37  pub compile_time: bool,
38  pub vis: Option<syn::Visibility>,
39  pub result: Option<GetterConverter>,
40}
41
42#[derive(FromMeta)]
43pub struct StructGetterOptions {
44  pub prefix: Option<syn::Ident>,
45  #[darling(default)]
46  pub style: Style,
47  #[darling(default, rename = "skip")]
48  pub ignore: bool,
49  pub vis_all: Option<syn::Visibility>,
50}
51
52impl Default for StructGetterOptions {
53  fn default() -> Self {
54    Self {
55      prefix: None,
56      style: Style::Ref,
57      ignore: false,
58      vis_all: None,
59    }
60  }
61}
62
63#[derive(Default, Clone)]
64pub struct FnGenerics {
65  pub bound: Option<syn::Generics>,
66}
67
68impl darling::FromMeta for FnGenerics {
69  fn from_value(value: &syn::Lit) -> darling::Result<Self> {
70    if let syn::Lit::Str(ref s) = value {
71      if s.value().is_empty() {
72        Ok(Self { bound: None })
73      } else {
74        let tt = format!("<{}>", s.value());
75        let bound = syn::parse_str::<syn::Generics>(&tt)?;
76        Ok(Self { bound: Some(bound) })
77      }
78    } else {
79      Err(darling::Error::custom("expected str literal").with_span(value))
80    }
81  }
82}
83
84#[derive(FromMeta, Clone)]
85pub struct GetterConverter {
86  #[darling(rename = "type")]
87  pub ty: Option<syn::Type>,
88  pub converter: FieldConverter,
89  #[darling(default)]
90  pub bound: FnGenerics,
91}
92
93impl GetterConverter {
94  pub fn to_getter_fn(
95    &self,
96    field_name: &syn::Ident,
97    field_ty: &syn::Type,
98    style: Style,
99    vis: &syn::Visibility,
100    fn_name: &syn::Ident,
101  ) -> proc_macro2::TokenStream {
102    let field_ty = self.ty.as_ref().unwrap_or(field_ty);
103    let bound = self.bound.bound.as_ref();
104    let result = match self.converter.style.unwrap_or(style) {
105      Style::Ref => match &self.converter.func {
106        Some(conv) => quote! {
107          #conv(&self.#field_name)
108        },
109        None => quote! {
110          &self.#field_name
111        },
112      },
113      Style::Move => match &self.converter.func {
114        Some(conv) => quote! {
115          #conv(self.#field_name)
116        },
117        None => quote! {
118          self.#field_name
119        },
120      },
121    };
122    match style {
123      Style::Ref => quote! {
124        #[inline]
125        #vis fn #fn_name #bound (&self) -> #field_ty {
126          #result
127        }
128      },
129      Style::Move => quote! {
130        #[inline]
131        #vis fn #fn_name #bound (self) ->  {
132          #result
133        }
134      },
135    }
136  }
137}
138
139pub struct FieldGetter {
140  pub field_name: syn::Ident,
141  pub field_ty: syn::Type,
142  pub style: Style,
143  pub vis: syn::Visibility,
144  pub compile_time: bool,
145  pub fn_name: syn::Ident,
146  pub attrs: Option<super::Attributes>,
147  pub converter: Option<GetterConverter>,
148}
149
150impl ToTokens for FieldGetter {
151  fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
152    let vis = &self.vis;
153    let fn_name = &self.fn_name;
154    let field_name = &self.field_name;
155    let field_ty = self
156      .converter
157      .as_ref()
158      .map(|conv| conv.ty.as_ref().unwrap_or(&self.field_ty))
159      .unwrap_or(&self.field_ty);
160
161    let compile_time = if self.compile_time {
162      quote! { const }
163    } else {
164      quote! {}
165    };
166
167    let attrs = &self.attrs;
168    match &self.converter {
169      Some(converter) => {
170        let bound = converter.bound.bound.as_ref();
171        let style = converter.converter.style.unwrap_or(self.style);
172        let result = match style {
173          Style::Ref => match &converter.converter.func {
174            Some(conv) => quote! {
175              #conv(&self.#field_name)
176            },
177            None => quote! {
178              &self.#field_name
179            },
180          },
181          Style::Move => match &converter.converter.func {
182            Some(conv) => quote! {
183              #conv(self.#field_name)
184            },
185            None => quote! {
186              self.#field_name
187            },
188          },
189        };
190
191        tokens.extend(match style {
192          Style::Ref => quote! {
193            #attrs
194            #[inline]
195            #vis #compile_time fn #fn_name #bound (&self) -> #field_ty {
196              #result
197            }
198          },
199          Style::Move => quote! {
200            #attrs
201            #[inline]
202            #vis #compile_time fn #fn_name #bound (self) -> #field_ty {
203              #result
204            }
205          },
206        });
207      }
208      None => {
209        let style = self.style;
210        tokens.extend(quote! {
211            #attrs
212            #[inline]
213            #vis #compile_time fn #fn_name(&self) -> #style #field_ty {
214              #style self.#field_name
215            }
216        });
217      }
218    }
219  }
220}