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 }
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}