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}