abstract_impl/
lib.rs

1#![allow(clippy::needless_doctest_main)]
2#![doc = include_str!("../README.md")]
3use core::panic;
4
5use quote::{quote, ToTokens};
6use syn::punctuated::Punctuated;
7use syn::spanned::Spanned;
8use syn::{parse_macro_input, ItemImpl, ItemTrait, Path};
9use syn::{token, Ident};
10mod change_self;
11mod dummy;
12mod mac;
13mod transform;
14
15struct IdentList(Punctuated<syn::Ident, token::Comma>);
16impl syn::parse::Parse for IdentList {
17    fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
18        Ok(IdentList(
19            input.parse_terminated(syn::Ident::parse, token::Comma)?,
20        ))
21    }
22}
23
24/// Define an abstract implementation for a trait, that types can use
25///
26/// ```
27/// # use abstract_impl::abstract_impl;
28/// # trait SomeTrait {
29/// #   fn some() -> Self;
30/// #   fn other(&self) -> String;
31/// # }
32/// #[abstract_impl]
33/// impl Impl for SomeTrait where Self: Default + std::fmt::Debug {
34///   fn some() -> Self {
35///     Self::default()
36///   }
37///   fn other(&self) -> String {
38///     // You have to use context here instead of self, because we don't change macro contents
39///     format!("{context:?}")
40///   }
41/// }
42/// impl_Impl!(());
43/// # fn main() {
44/// # assert_eq!("()", <()>::some().other());
45/// # }
46/// ```
47#[proc_macro_attribute]
48pub fn abstract_impl(
49    _attr: proc_macro::TokenStream,
50    item: proc_macro::TokenStream,
51) -> proc_macro::TokenStream {
52    let parsed = parse_macro_input!(item as ItemImpl);
53    let IdentList(attrs) = parse_macro_input!(_attr);
54    let res = match transform::transform(
55        parsed,
56        attrs.iter().all(|attr| *attr != "no_dummy"),
57        attrs.iter().all(|attr| *attr != "no_macro"),
58        attrs.iter().any(|attr| *attr == "legacy_order"),
59    ) {
60        Ok(res) => res,
61        Err(e) => return e.into_compile_error().into(),
62    };
63    res.to_token_stream().into()
64}
65
66/// Generates a TyType trait (has type Ty) with a generic TyUsingType<T> impl given a type name Ty.
67/// ```rust
68/// # use abstract_impl::type_trait;
69/// type_trait!(Ty);
70/// struct Test;
71/// impl_TyUsingType!(<u8> Test);
72/// fn main() {
73///   let x: <Test as TyType>::Ty = 5u8;
74/// # assert_eq!(x, 5);
75/// }
76/// ```
77#[proc_macro]
78pub fn type_trait(item: proc_macro::TokenStream) -> proc_macro::TokenStream {
79    let ty = parse_macro_input!(item as Ident);
80    let trait_name = Ident::new(&format!("{ty}Type"), ty.span());
81    let impl_name = Ident::new(&format!("{ty}UsingType"), ty.span());
82    quote! {
83        pub trait #trait_name {
84            type #ty;
85        }
86        #[::abstract_impl::abstract_impl(no_dummy)]
87        impl #impl_name<T> for #trait_name {
88            type #ty = T;
89        }
90    }
91    .into()
92}
93
94/// ```rust
95/// # use abstract_impl::use_type;
96/// #[use_type]
97/// trait SomeTrait {
98///   type Ty: Default;
99///   fn get_ty() -> Self::Ty {
100///     Self::Ty::default()
101///   }
102/// }
103/// # fn main() {}
104/// ```
105#[proc_macro_attribute]
106pub fn use_type(
107    _attr: proc_macro::TokenStream,
108    item: proc_macro::TokenStream,
109) -> proc_macro::TokenStream {
110    use syn::ImplItem;
111    use syn::TraitItem;
112    use syn::Type;
113    let trait_ = parse_macro_input!(item as ItemTrait);
114    let name = trait_.ident.clone();
115    let items = trait_
116        .items
117        .clone()
118        .into_iter()
119        .filter_map(|item| match item {
120            TraitItem::Type(syn::TraitItemType {
121                attrs,
122                type_token,
123                ident,
124                generics,
125                semi_token,
126                ..
127            }) => {
128                let new_ident = Ident::new(&format!("_use_type_{ident}"), ident.span());
129                Some(Ok((
130                    ImplItem::Type(syn::ImplItemType {
131                        attrs,
132                        vis: syn::Visibility::Inherited,
133                        defaultness: None,
134                        type_token,
135                        ident,
136                        generics,
137                        eq_token: syn::token::Eq::default(),
138                        ty: Type::Path(syn::TypePath {
139                            qself: None,
140                            path: Path::from(new_ident.clone()),
141                        }),
142                        semi_token,
143                    }),
144                    new_ident,
145                )))
146            }
147            TraitItem::Const(syn::TraitItemConst {
148                attrs,
149                const_token,
150                ident,
151                generics,
152                colon_token,
153                ty,
154                semi_token,
155                ..
156            }) => {
157                let new_ident = Ident::new(&format!("_use_type_{ident}"), ident.span());
158                Some(Ok((
159                    ImplItem::Const(syn::ImplItemConst {
160                        attrs,
161                        vis: syn::Visibility::Inherited,
162                        defaultness: None,
163                        const_token,
164                        ident,
165                        generics,
166                        colon_token,
167                        ty,
168                        eq_token: syn::token::Eq::default(),
169                        expr: syn::Expr::Path(syn::ExprPath {
170                            attrs: vec![],
171                            qself: None,
172                            path: Path::from(new_ident.clone()),
173                        }),
174                        semi_token,
175                    }),
176                    new_ident,
177                )))
178            }
179            TraitItem::Fn(syn::TraitItemFn {
180                default: Some(_), ..
181            }) => None,
182            o => Some(Err(syn::Error::new(o.span(), "cannot implement functions"))),
183        })
184        .collect::<Result<(Vec<_>, Vec<_>), _>>();
185    let (items, item_names) = match items {
186        Ok(items) => items,
187        Err(err) => return err.into_compile_error().into(),
188    };
189    let impl_name = Ident::new(&format!("{name}UsingType"), name.span());
190    quote! {
191        #trait_
192        #[allow(non_camel_case_types)]
193        #[::abstract_impl::abstract_impl]
194        impl #impl_name<#(#item_names),*> for #name {
195            #(#items)*
196        }
197    }
198    .into()
199}
200
201/// ```rust
202/// # use abstract_impl::use_field;
203/// #[use_field]
204/// trait SomeTrait {
205///   fn field(&mut self) -> &mut str;
206/// }
207/// struct Test {
208///   field: String,
209/// }
210/// impl_SomeTrait_with_field!(Test {field});
211/// ```
212#[proc_macro_attribute]
213pub fn use_field(
214    _attr: proc_macro::TokenStream,
215    item: proc_macro::TokenStream,
216) -> proc_macro::TokenStream {
217    use syn::ImplItem;
218    use syn::TraitItem;
219    let trait_ = parse_macro_input!(item as ItemTrait);
220    let name = trait_.ident.clone();
221    let macro_name = Ident::new(&format!("impl_{name}_with_field"), name.span());
222    let items = trait_.items.clone().into_iter().map(|item| match item {
223        TraitItem::Fn(syn::TraitItemFn { attrs, sig, .. }) => {
224            let (ref_, mut_) = match sig.inputs.first() {
225                Some(syn::FnArg::Receiver(r)) => (r.reference.clone(), r.mutability),
226                _ => panic!("All functions must take self"),
227            };
228            ImplItem::Fn(syn::ImplItemFn {
229                attrs,
230                vis: syn::Visibility::Inherited,
231                defaultness: None,
232                sig,
233                block: syn::Block {
234                    brace_token: syn::token::Brace::default(),
235                    stmts: vec![syn::Stmt::Expr(
236                        syn::Expr::Verbatim(match (ref_, mut_) {
237                            (Some((ref_, _)), Some(mut_)) => quote! {#ref_ #mut_ self.$e},
238                            (Some((ref_, _)), None) => quote! {#ref_ self.$e},
239                            _ => quote! {self.$e},
240                        }),
241                        None,
242                    )],
243                },
244            })
245        }
246        _ => panic!("Only functions can be used with use_field"),
247    });
248    quote! {
249        #trait_
250        #[macro_export]
251        macro_rules! #macro_name {
252            ($t:ty {$e:ident}) => {
253                impl #name for $t {
254                    #(#items)*
255                }
256            }
257        }
258    }
259    .into()
260}
261
262#[allow(dead_code)]
263struct ImplWithFieldInput {
264    lt_token: syn::token::Lt,
265    type_: syn::Type,
266    gt_token: syn::token::Gt,
267    self_: syn::Type,
268    brace_token: syn::token::Brace,
269    expr: syn::Expr,
270}
271impl syn::parse::Parse for ImplWithFieldInput {
272    fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
273        let content;
274        Ok(Self {
275            lt_token: input.parse()?,
276            type_: input.parse()?,
277            gt_token: input.parse()?,
278            self_: input.parse()?,
279            brace_token: syn::braced!(content in input),
280            expr: content.parse()?,
281        })
282    }
283}
284
285/// Implement AsRef using a field
286/// ```rust
287/// # use abstract_impl::*;
288/// struct Test {
289///   field: String,
290/// };
291/// impl_as_ref_with_field!(<String> Test {field});
292/// ```
293#[proc_macro]
294pub fn impl_as_ref_with_field(item: proc_macro::TokenStream) -> proc_macro::TokenStream {
295    let ImplWithFieldInput {
296        type_, self_, expr, ..
297    } = parse_macro_input!(item as ImplWithFieldInput);
298    quote! {
299        impl AsRef<#type_> for #self_ {
300            fn as_ref(&self) -> &#type_ {
301                &self.#expr
302            }
303        }
304    }
305    .into()
306}
307/// Implement AsMut using a field
308/// ```rust
309/// # use abstract_impl::*;
310/// struct Test {
311///   field: String,
312/// };
313/// impl_as_mut_with_field!(<String> Test {field});
314/// ```
315#[proc_macro]
316pub fn impl_as_mut_with_field(item: proc_macro::TokenStream) -> proc_macro::TokenStream {
317    let ImplWithFieldInput {
318        type_, self_, expr, ..
319    } = parse_macro_input!(item as ImplWithFieldInput);
320    quote! {
321        impl AsMut<#type_> for #self_ {
322            fn as_mut(&mut self) -> &mut #type_ {
323                &mut self.#expr
324            }
325        }
326    }
327    .into()
328}
329/// Implement Into using a field
330/// ```rust
331/// # use abstract_impl::*;
332/// struct Test {
333///   field: String,
334/// };
335/// impl_into_with_field!(<String> Test {field});
336/// ```
337#[proc_macro]
338pub fn impl_into_with_field(item: proc_macro::TokenStream) -> proc_macro::TokenStream {
339    let ImplWithFieldInput {
340        type_, self_, expr, ..
341    } = parse_macro_input!(item as ImplWithFieldInput);
342    quote! {
343        impl Into<#type_> for #self_ {
344            fn into(self) -> #type_ {
345                self.#expr
346            }
347        }
348    }
349    .into()
350}
351/// Implement Into, AsRef and AsMut using a field
352/// ```rust
353/// # use abstract_impl::*;
354/// struct Test {
355///   field: String,
356/// };
357/// impl_conversion_with_field!(<String> Test {field});
358/// ```
359#[proc_macro]
360pub fn impl_conversion_with_field(item: proc_macro::TokenStream) -> proc_macro::TokenStream {
361    let item: proc_macro2::TokenStream = item.into();
362    quote! {
363        ::abstract_impl::impl_into_with_field!(#item);
364        ::abstract_impl::impl_as_ref_with_field!(#item);
365        ::abstract_impl::impl_as_mut_with_field!(#item);
366    }
367    .into()
368}
369
370// DOCTESTS(hidden):
371/// ```rust
372/// use abstract_impl::abstract_impl;
373/// trait TimeType {
374///   type Time;
375/// }
376/// #[abstract_impl(no_dummy)]
377/// impl TimeUsingType<T> for TimeType {
378///   type Time = T;
379/// }
380/// impl_TimeUsingType!(<usize> ());
381/// fn main() {}
382/// ```
383#[allow(dead_code)]
384struct Tests;