[go: up one dir, main page]

ghost/
variance.rs

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}