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#[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#[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#[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#[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#[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#[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#[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#[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#[allow(dead_code)]
384struct Tests;