ownit_derive/
lib.rs

1use proc_macro2::{Ident, Span, TokenStream};
2use quote::{quote, ToTokens, TokenStreamExt};
3use syn::{parse_macro_input, ConstParam, DataStruct, DeriveInput, TypeParam, Variant};
4
5#[proc_macro_derive(Ownit)]
6pub fn derive_ownit(item: proc_macro::TokenStream) -> proc_macro::TokenStream {
7    let item = parse_macro_input!(item as DeriveInput);
8    let lifetimes: Vec<_> = item.generics.lifetimes().collect();
9
10    let name = item.ident;
11    match item.data {
12        syn::Data::Struct(DataStruct { fields, .. }) => match fields {
13            syn::Fields::Named(fields) => {
14                let fields = fields.named.iter().map(|f| f.ident.as_ref().unwrap());
15                generate_for_named_struct(
16                    &name,
17                    lifetimes.len(),
18                    &item.generics.type_params().collect::<Vec<_>>(),
19                    &item.generics.const_params().collect::<Vec<_>>(),
20                    &fields.collect::<Vec<_>>(),
21                )
22                .into()
23            }
24            syn::Fields::Unnamed(fields) => {
25                let fields = fields.unnamed.len();
26                generate_for_tuple_struct(
27                    &name,
28                    lifetimes.len(),
29                    &item.generics.type_params().collect::<Vec<_>>(),
30                    &item.generics.const_params().collect::<Vec<_>>(),
31                    fields,
32                )
33                .into()
34            }
35            syn::Fields::Unit => {
36                generate_for_unit_struct(&name, &item.generics.const_params().collect::<Vec<_>>())
37                    .into()
38            }
39        },
40        syn::Data::Enum(enm) => {
41            let variants = enm.variants.iter().collect::<Vec<_>>();
42            generate_for_enum(
43                &name,
44                lifetimes.len(),
45                &item.generics.type_params().collect::<Vec<_>>(),
46                &item.generics.const_params().collect::<Vec<_>>(),
47                &variants,
48            )
49            .into()
50        }
51        syn::Data::Union(_) => panic!("Ownit may not be implemented for `union` types"),
52    }
53}
54
55#[derive(Debug, Copy, Clone)]
56struct Void;
57
58impl ToTokens for Void {
59    fn to_tokens(&self, _tokens: &mut TokenStream) {
60        // Purposely empty
61    }
62}
63
64fn generate_for_named_struct(
65    name: &Ident,
66    lifetimes: usize,
67    generics: &[&TypeParam],
68    const_params: &[&ConstParam],
69    fields: &[&Ident],
70) -> TokenStream {
71    let life: Vec<_> = std::iter::repeat(Void).take(lifetimes).collect();
72    if !const_params.is_empty() {
73        unimplemented!("const params are not yet supported");
74    }
75
76    let generic_names: Vec<_> = generics.iter().map(|g| &g.ident).collect();
77    quote! {
78        impl<#(#generics + 'static,)*>::ownit::Ownit for #name<#(#life '_,)*#(#generic_names,)*> {
79            type OwnedSelf = #name<#(#life 'static,)*#(#generic_names,)*>;
80
81            fn into_static(self) -> Self::OwnedSelf {
82                use ::ownit::Ownit;
83                #name {
84                    #(#fields: self.#fields.into_static(),)*
85                }
86            }
87        }
88    }
89}
90
91fn generate_for_tuple_struct(
92    name: &Ident,
93    lifetimes: usize,
94    generics: &[&TypeParam],
95    const_params: &[&ConstParam],
96    fields: usize,
97) -> TokenStream {
98    let life: Vec<_> = std::iter::repeat(Void).take(lifetimes).collect();
99    if !const_params.is_empty() {
100        unimplemented!("const params are not yet supported");
101    }
102    let fields = (0..fields).map(syn::Index::from);
103    let generic_names: Vec<_> = generics.iter().map(|g| &g.ident).collect();
104    quote! {
105        impl<#(#generics + 'static,)*> ::ownit::Ownit for #name<#(#life '_,)*#(#generic_names,)*> {
106            type OwnedSelf = #name<#(#life 'static,)*#(#generic_names,)*>;
107
108            fn into_static(self) -> Self::OwnedSelf {
109                use ::ownit::Ownit;
110                #name (
111                    #(self.#fields.into_static(),)*
112                )
113            }
114        }
115    }
116}
117
118fn generate_for_unit_struct(name: &Ident, const_params: &[&ConstParam]) -> TokenStream {
119    if !const_params.is_empty() {
120        unimplemented!("const params are not yet supported");
121    }
122    quote! {
123        impl ::ownit::Ownit for #name {
124            type OwnedSelf = #name;
125
126            fn into_static(self) -> Self::OwnedSelf {
127                #name
128            }
129        }
130    }
131}
132
133struct MatchArm<'a> {
134    variant: &'a Variant,
135}
136
137impl ToTokens for MatchArm<'_> {
138    fn to_tokens(&self, tokens: &mut TokenStream) {
139        let name = &self.variant.ident;
140        let lhs = {
141            match &self.variant.fields {
142                syn::Fields::Named(f) => {
143                    let fields = f.named.iter().map(|f| f.ident.as_ref().unwrap());
144                    quote! {
145                        {#(#fields,)*}
146                    }
147                }
148                syn::Fields::Unnamed(f) => {
149                    let fields = f.unnamed.iter().count();
150                    let names =
151                        (0..fields).map(|i| syn::Ident::new(&format!("x{i}"), Span::call_site()));
152                    quote! {
153                        (#(#names,)*)
154                    }
155                }
156                syn::Fields::Unit => quote! {},
157            }
158        };
159        let rhs = {
160            match &self.variant.fields {
161                syn::Fields::Named(f) => {
162                    let fields = f.named.iter().map(|f| f.ident.as_ref().unwrap());
163                    quote! {
164                        {#(#fields: #fields.into_static(),)*}
165                    }
166                }
167                syn::Fields::Unnamed(f) => {
168                    let fields = f.unnamed.iter().count();
169                    let names =
170                        (0..fields).map(|i| syn::Ident::new(&format!("x{i}"), Span::call_site()));
171                    quote! {
172                        (#(#names.into_static(),)*)
173                    }
174                }
175                syn::Fields::Unit => quote! {},
176            }
177        };
178        tokens.append_all(quote! {
179            Self::#name #lhs => Self::OwnedSelf::#name #rhs
180        })
181    }
182}
183
184fn generate_for_enum(
185    name: &Ident,
186    lifetimes: usize,
187    generics: &[&TypeParam],
188    const_params: &[&ConstParam],
189    variants: &[&Variant],
190) -> TokenStream {
191    let life: Vec<_> = std::iter::repeat(Void).take(lifetimes).collect();
192    if !const_params.is_empty() {
193        unimplemented!("const params are not yet supported");
194    }
195    let generic_names: Vec<_> = generics.iter().map(|g| &g.ident).collect();
196    let matches = variants.iter().map(|variant| MatchArm { variant });
197    quote! {
198        impl<#(#generics + 'static,)*> ::ownit::Ownit for #name<#(#life '_,)*#(#generic_names,)*> {
199            type OwnedSelf = #name<#(#life 'static,)*#(#generic_names,)*>;
200
201            fn into_static(self) -> Self::OwnedSelf {
202                use ::ownit::Ownit;
203
204                match self {
205                    #(#matches,)*
206                }
207            }
208        }
209    }
210}