1use proc_macro2::{Ident, TokenStream};
2use quote::quote;
3use syn::{Attribute, LifetimeParam, Meta, TypeParam};
4
5enum Variance {
6 Covariant,
7 Contravariant,
8 Invariant,
9}
10
11pub trait HasVarianceAttribute {
12 fn attrs(&mut self) -> &mut Vec<Attribute>;
13}
14
15impl HasVarianceAttribute for TypeParam {
16 fn attrs(&mut self) -> &mut Vec<Attribute> {
17 &mut self.attrs
18 }
19}
20
21impl HasVarianceAttribute for LifetimeParam {
22 fn attrs(&mut self) -> &mut Vec<Attribute> {
23 &mut self.attrs
24 }
25}
26
27pub fn apply(
28 param: &mut dyn HasVarianceAttribute,
29 base: TokenStream,
30 type_param: &Ident,
31) -> TokenStream {
32 let mut variance = Variance::Covariant;
33
34 let attrs = param.attrs();
35 *attrs = attrs
36 .drain(..)
37 .filter(|attr| {
38 if let Meta::Path(attr_path) = &attr.meta {
39 if attr_path.is_ident("contra") {
40 variance = Variance::Contravariant;
41 return false;
42 } else if attr_path.is_ident("invariant") {
43 variance = Variance::Invariant;
44 return false;
45 }
46 }
47 true
48 })
49 .collect();
50
51 let phantom = quote!(self::#type_param<#base>);
52 match variance {
53 Variance::Covariant => base,
54 Variance::Contravariant => quote!(fn(#phantom)),
55 Variance::Invariant => quote!(fn(#phantom) -> #phantom),
56 }
57}