1use proc_macro::TokenStream as TokenStream1;
81use proc_macro2::Span;
82use proc_macro2::TokenStream;
83use quote::ToTokens;
84use quote::quote;
85use syn::Visibility;
86use syn::spanned::Spanned;
87
88#[proc_macro_derive(Getters, attributes(getters))]
104pub fn derive_getter_methods(input: TokenStream1) -> TokenStream1 {
105 getters(input.into()).unwrap_or_else(|e| e.into_compile_error()).into()
106}
107
108macro_rules! ident_str {
110 ($meta:ident ) => {
111 $meta.path.get_ident().map(|i| i.to_string()).unwrap_or_default().as_str()
112 };
113}
114
115macro_rules! error {
117 ($msg:literal $(,$e:expr)*) => {
118 syn::Error::new(proc_macro2::Span::call_site(), format!($msg $(,$e)*))
119 }
120}
121
122macro_rules! generic {
124 ($segment:ident) => {{
125 let syn::PathArguments::AngleBracketed(angle_generic) = &$segment.arguments else {
126 return Err(error!("Unparseable type: {}", $segment.to_token_stream().to_string()));
127 };
128 angle_generic.args.clone()
129 }};
130}
131
132fn getters(input: TokenStream) -> syn::Result<TokenStream> {
133 let mut getters: Vec<TokenStream> = vec![];
134
135 let struct_ = syn::parse2::<syn::ItemStruct>(input)?;
137
138 let struct_opts = {
140 let mut struct_opts = StructOptions::default();
141 if let Some(attr) = struct_.attrs.iter().find(|a| a.path().is_ident("getters")) {
142 attr.parse_nested_meta(|meta| {
143 match ident_str!(meta) {
144 "copy" => struct_opts.copy = true,
145 "vis" => struct_opts.vis = meta.value()?.parse::<Visibility>()?,
146 other => Err(error!("Invalid option: {}", other))?,
147 };
148 Ok(())
149 })?;
150 };
151 struct_opts
152 };
153
154 for field in struct_.fields {
156 let field_opts = {
158 let mut field_opts = struct_opts.field_opts();
159 if let Some(getters_attr) = field.attrs.iter().find(|a| a.path().is_ident("getters")) {
160 getters_attr.parse_nested_meta(|meta| {
161 match ident_str!(meta) {
162 "copy" => field_opts.copy = true,
163 "skip" => field_opts.skip = true,
164 "vis" => field_opts.vis = meta.value()?.parse::<Visibility>()?,
165 other => Err(error!("Invalid option: {}", other))?,
166 }
167 Ok(())
168 })?;
169 }
170 if field_opts.skip {
171 continue;
172 }
173 field_opts
174 };
175
176 let doc = {
178 let mut answer = String::new();
179 for doc_attr in field.attrs.iter().filter(|d| d.path().is_ident("doc")) {
180 if let syn::Meta::NameValue(nv) = &doc_attr.meta {
181 if let syn::Expr::Lit(l) = &nv.value {
182 if let syn::Lit::Str(s) = &l.lit {
183 if !answer.is_empty() {
184 answer += "\n";
185 }
186 answer += s.value().as_str();
187 }
188 }
189 }
190 }
191 match answer.is_empty() {
192 true => quote! {},
193 false => quote! { #[doc = #answer] },
194 }
195 };
196
197 let span = field.span();
199 let field_ident =
200 &field.ident.ok_or_else(|| syn::Error::new(span, "Fields must be named."))?;
201 let field_type = &field.ty;
202 let vis = &field_opts.vis;
203 let mut const_ = quote! { const };
204 let (return_type, getter_impl) = match field_type {
205 syn::Type::Path(p) => {
206 let Some(segment) = &p.path.segments.last() else { Err(error!("Unparseable type."))? };
207 match segment.ident.to_string().as_str() {
208 "i8" | "i16" | "i32" | "i64" | "i128" | "isize" | "u8" | "u16" | "u32" | "u64"
210 | "u128" | "usize" | "f32" | "f64" | "char" =>
211 (quote! { #field_type }, quote! { self.#field_ident }),
212 "String" => (quote! { &str }, quote! { self.#field_ident.as_str() }),
214 "Vec" => {
216 let ty = generic!(segment);
217 (quote! { &[#ty] }, quote! { self.#field_ident.as_slice() })
218 },
219 "Box" => {
221 let ty = generic!(segment);
222 (quote! { &#ty }, quote! { &self.#field_ident })
223 },
224 "Option" => {
226 let ty = generic!(segment);
227 match field_opts.copy {
228 true => (quote! { Option<#ty> }, quote! { self.#field_ident }),
229 false => (quote! { Option<&#ty> }, quote! { self.#field_ident.as_ref() }),
230 }
231 },
232 "Rc" => {
234 let ty = generic!(segment);
235 const_ = quote! {};
236 (quote! { ::std::rc::Rc<#ty> }, quote! { ::std::rc::Rc::clone(&self.#field_ident) })
237 },
238 "Arc" => {
239 let ty = generic!(segment);
240 const_ = quote! {};
241 (
242 quote! { ::std::sync::Arc<#ty> },
243 quote! { ::std::sync::Arc::clone(&self.#field_ident)},
244 )
245 },
246 _ => match field_opts.copy {
248 true => (quote! { #field_type }, quote! { self.#field_ident }),
249 false => (quote! { &#field_type }, quote! { &self.#field_ident }),
250 },
251 }
252 },
253 syn::Type::Reference(ref_) => (quote! { #ref_ }, quote! { self.#field_ident }),
255 syn::Type::Ptr(ptr) => (quote! { #ptr }, quote! { self.#field_ident }),
256 _ => (quote! { &#field_type }, quote! { &self.#field_ident }),
257 };
258 getters.push(quote! {
259 #doc #[inline]
260 #vis #const_ fn #field_ident(&self) -> #return_type {
261 #getter_impl
262 }
263 });
264 }
265
266 let ident = &struct_.ident;
268 let (impl_generics, ty_generics, where_clause) = &struct_.generics.split_for_impl();
269 Ok(quote! {
270 #[automatically_derived]
271 impl #impl_generics #ident #ty_generics #where_clause {
272 #(#getters)*
273 }
274 })
275}
276
277struct StructOptions {
279 copy: bool,
280 vis: Visibility,
281}
282
283impl StructOptions {
284 fn field_opts(&self) -> FieldOptions {
286 FieldOptions { copy: self.copy, skip: false, vis: self.vis.clone() }
287 }
288}
289
290impl Default for StructOptions {
291 fn default() -> Self {
292 Self { copy: false, vis: Visibility::Public(syn::Token)) }
293 }
294}
295
296struct FieldOptions {
298 copy: bool,
299 skip: bool,
300 vis: Visibility,
301}