trait_tactics_macros/
lib.rs1use std::mem;
2
3use proc_macro::TokenStream;
4use quote::ToTokens;
5use syn::{
6 meta::ParseNestedMeta,
7 parse::{Nothing, Parse, ParseStream},
8 parse_macro_input, parse_quote,
9 spanned::Spanned,
10 GenericArgument, ItemImpl, Path, PathArguments, ReturnType, Type,
11};
12
13mod binop;
16
17#[proc_macro_attribute]
18pub fn assign_via_binop_ref_lhs(args: TokenStream, item: TokenStream) -> TokenStream {
19 binop::assign_via_binop_ref_lhs_attr(args, item)
20}
21#[proc_macro_attribute]
22pub fn assign_via_assign_ref(args: TokenStream, item: TokenStream) -> TokenStream {
23 binop::assign_via_assign_ref_attr(args, item)
24}
25#[proc_macro_attribute]
26pub fn binop_via_assign(args: TokenStream, item: TokenStream) -> TokenStream {
27 binop::binop_via_assign_attr(args, item)
28}
29#[proc_macro_attribute]
30pub fn binop_via_binop_ref_rhs(args: TokenStream, item: TokenStream) -> TokenStream {
31 binop::binop_via_binop_ref_rhs_attr(args, item)
32}
33#[proc_macro_attribute]
34pub fn binop_via_binop_ref_lhs(args: TokenStream, item: TokenStream) -> TokenStream {
35 binop::binop_via_binop_ref_lhs_attr(args, item)
36}
37
38#[proc_macro_attribute]
41pub fn partial_ord_via_ord(args: TokenStream, item: TokenStream) -> TokenStream {
42 parse_macro_input!(args as Nothing);
43 let ItemImplEmpty(mut item_impl) = parse_macro_input!(item);
44 item_impl.items.push(parse_quote! {
45 fn partial_cmp(&self, other: &Self) -> ::std::option::Option<::std::cmp::Ordering> {
46 ::std::option::Option::Some(::std::cmp::Ord::cmp(self, other))
47 }
48 });
49 item_impl.into_token_stream().into()
50}
51
52#[cfg(feature = "num-traits")]
53#[proc_macro_attribute]
54pub fn sum_via_fold_zero_add(args: TokenStream, item: TokenStream) -> TokenStream {
55 parse_macro_input!(args as Nothing);
56 let ItemImplEmpty(mut item_impl) = parse_macro_input!(item);
57 item_impl.items.push(parse_quote! {
58 fn sum<I: ::std::iter::Iterator<Item = Self>>(iter: I) -> Self {
59 iter.fold(::num_traits::Zero::zero(), ::std::ops::Add::add)
60 }
61 });
62 item_impl.into_token_stream().into()
63}
64
65fn parse_meta_value_into_arg<T: Parse>(
68 meta: &ParseNestedMeta<'_>,
69 arg: &mut Option<T>,
70) -> syn::Result<()> {
71 if arg.is_some() {
72 return Err(meta.error("conflicting argument"));
73 }
74 *arg = Some(meta.value()?.parse()?);
75 Ok(())
76}
77
78fn extract_trait_from_impl(item_impl: &ItemImpl) -> syn::Result<&Path> {
79 match &item_impl.trait_ {
80 Some((None, tr, _)) => Ok(tr),
81 Some((Some(not), _, _)) => {
82 Err(syn::Error::new(not.span, "negative implementations are not allowed"))
83 }
84 None => Err(syn::Error::new(item_impl.span(), "must be a trait implementation")),
85 }
86}
87
88fn strip_trait_operand_type(tr: &mut Path) -> syn::Result<Type> {
93 let path_args = mem::replace(
94 &mut tr.segments.last_mut().expect("path cannot be empty").arguments,
95 PathArguments::None,
96 );
97 Ok(match path_args {
98 PathArguments::None => None,
99 PathArguments::AngleBracketed(list) => {
100 let mut it = list.args.iter();
101 if let Some(arg) = it.next() {
102 if it.next().is_some() {
103 return Err(syn::Error::new(
104 tr.span(),
105 "expected exactly one generic argument",
106 ));
107 }
108 if let GenericArgument::Type(ty) = arg {
109 Some(ty.clone())
110 } else {
111 return Err(syn::Error::new(arg.span(), "generic argument must be a type"));
112 }
113 } else {
114 None
115 }
116 }
117 _ => return Err(syn::Error::new(tr.span(), "generic arguments must be angle-bracketed")),
118 }
119 .unwrap_or(syn::parse_quote!(Self)))
120}
121
122trait ReturnTypeExt {
123 fn into_type(self) -> Type;
124}
125impl ReturnTypeExt for ReturnType {
126 fn into_type(self) -> Type {
127 match self {
128 ReturnType::Default => syn::parse_quote! { () },
129 ReturnType::Type(_, ty) => *ty,
130 }
131 }
132}
133
134struct ItemImplEmpty(ItemImpl);
136impl Parse for ItemImplEmpty {
137 fn parse(input: ParseStream) -> syn::Result<Self> {
138 let item_impl = input.parse::<ItemImpl>()?;
139 if !item_impl.items.is_empty() {
140 return Err(syn::Error::new(
141 item_impl.brace_token.span.join(),
142 "impl body must be empty",
143 ));
144 }
145 Ok(Self(item_impl))
146 }
147}