educe/common/
where_predicates_bool.rs

1use quote::{quote, ToTokens};
2use syn::{
3    parse::{Parse, ParseStream},
4    punctuated::Punctuated,
5    spanned::Spanned,
6    token::Comma,
7    Expr, GenericParam, Lit, Meta, MetaNameValue, Path, Token, Type, WherePredicate,
8};
9
10use super::path::path_to_string;
11
12pub(crate) type WherePredicates = Punctuated<WherePredicate, Token![,]>;
13
14pub(crate) enum WherePredicatesOrBool {
15    WherePredicates(WherePredicates),
16    Bool(bool),
17    All,
18}
19
20impl WherePredicatesOrBool {
21    fn from_lit(lit: &Lit) -> syn::Result<Self> {
22        Ok(match lit {
23            Lit::Bool(lit) => Self::Bool(lit.value),
24            Lit::Str(lit) => match lit.parse_with(WherePredicates::parse_terminated) {
25                Ok(where_predicates) => Self::WherePredicates(where_predicates),
26                Err(_) if lit.value().is_empty() => Self::Bool(false),
27                Err(error) => return Err(error),
28            },
29            other => {
30                return Err(syn::Error::new(
31                    other.span(),
32                    "unexpected kind of literal (only boolean or string allowed)",
33                ))
34            },
35        })
36    }
37}
38
39impl Parse for WherePredicatesOrBool {
40    #[inline]
41    fn parse(input: ParseStream) -> syn::Result<Self> {
42        if let Ok(lit) = input.parse::<Lit>() {
43            return Self::from_lit(&lit);
44        }
45
46        if let Ok(_star) = input.parse::<Token![*]>() {
47            return Ok(Self::All);
48        }
49
50        Ok(Self::WherePredicates(input.parse_terminated(WherePredicate::parse, Token![,])?))
51    }
52}
53
54#[inline]
55pub(crate) fn meta_name_value_2_where_predicates_bool(
56    name_value: &MetaNameValue,
57) -> syn::Result<WherePredicatesOrBool> {
58    if let Expr::Lit(lit) = &name_value.value {
59        return WherePredicatesOrBool::from_lit(&lit.lit);
60    }
61
62    Err(syn::Error::new(
63        name_value.value.span(),
64        format!(
65            "expected `{path} = \"where_predicates\"` or `{path} = false`",
66            path = path_to_string(&name_value.path)
67        ),
68    ))
69}
70
71#[inline]
72pub(crate) fn meta_2_where_predicates(meta: &Meta) -> syn::Result<WherePredicatesOrBool> {
73    match &meta {
74        Meta::NameValue(name_value) => meta_name_value_2_where_predicates_bool(name_value),
75        Meta::List(list) => list.parse_args::<WherePredicatesOrBool>(),
76        Meta::Path(path) => Err(syn::Error::new(
77            path.span(),
78            format!(
79                "expected `{path} = \"where_predicates\"`, `{path}(where_predicates)`, `{path} = \
80                 false`, or `{path}(false)`",
81                path = path.clone().into_token_stream()
82            ),
83        )),
84    }
85}
86
87#[inline]
88pub(crate) fn create_where_predicates_from_all_generic_parameters(
89    params: &Punctuated<GenericParam, Comma>,
90    bound_trait: &Path,
91) -> WherePredicates {
92    let mut where_predicates = Punctuated::new();
93
94    for param in params {
95        if let GenericParam::Type(ty) = param {
96            let ident = &ty.ident;
97
98            where_predicates.push(syn::parse2(quote! { #ident: #bound_trait }).unwrap());
99        }
100    }
101
102    where_predicates
103}
104
105#[inline]
106pub(crate) fn create_where_predicates_from_generic_parameters_check_types(
107    bound_trait: &Path,
108    types: &[&Type],
109    supertraits: &[proc_macro2::TokenStream],
110) -> WherePredicates {
111    let mut where_predicates = Punctuated::new();
112
113    for t in types {
114        where_predicates.push(syn::parse2(quote! { #t: #bound_trait }).unwrap());
115    }
116
117    for supertrait in supertraits {
118        where_predicates.push(syn::parse2(quote! { Self: #supertrait }).unwrap());
119    }
120
121    where_predicates
122}