educe/common/
ident_bool.rs

1use syn::{
2    parse::{Parse, ParseStream},
3    spanned::Spanned,
4    Expr, Ident, Lit, LitBool, LitStr, Meta, MetaNameValue,
5};
6
7use super::path::path_to_string;
8
9#[derive(Debug)]
10pub(crate) enum IdentOrBool {
11    Ident(Ident),
12    Bool(bool),
13}
14
15impl Parse for IdentOrBool {
16    #[inline]
17    fn parse(input: ParseStream) -> syn::Result<Self> {
18        if let Ok(lit) = input.parse::<Lit>() {
19            match lit {
20                Lit::Bool(lit) => return Ok(Self::Bool(lit.value)),
21                Lit::Str(lit) => {
22                    return match lit.parse::<Ident>() {
23                        Ok(ident) => Ok(Self::Ident(ident)),
24                        Err(_) if lit.value().is_empty() => Ok(Self::Bool(false)),
25                        Err(error) => Err(error),
26                    }
27                },
28                _ => (),
29            }
30        }
31
32        Ok(Self::Ident(input.parse::<Ident>()?))
33    }
34}
35
36#[inline]
37pub(crate) fn meta_name_value_2_ident(name_value: &MetaNameValue) -> syn::Result<Ident> {
38    match &name_value.value {
39        Expr::Lit(lit) => {
40            if let Lit::Str(lit) = &lit.lit {
41                return lit.parse();
42            }
43        },
44        Expr::Path(path) => {
45            if let Some(ident) = path.path.get_ident() {
46                return Ok(ident.clone());
47            }
48        },
49        _ => (),
50    }
51
52    Err(syn::Error::new(
53        name_value.value.span(),
54        format!("expected `{path} = Ident`", path = path_to_string(&name_value.path)),
55    ))
56}
57
58#[inline]
59pub(crate) fn meta_2_ident(meta: &Meta) -> syn::Result<Ident> {
60    match &meta {
61        Meta::NameValue(name_value) => meta_name_value_2_ident(name_value),
62        Meta::List(list) => {
63            if let Ok(lit) = list.parse_args::<LitStr>() {
64                lit.parse()
65            } else {
66                list.parse_args()
67            }
68        },
69        Meta::Path(path) => Err(syn::Error::new(
70            path.span(),
71            format!("expected `{path} = Ident` or `{path}(Ident)`", path = path_to_string(path)),
72        )),
73    }
74}
75
76#[inline]
77pub(crate) fn meta_name_value_2_bool(name_value: &MetaNameValue) -> syn::Result<bool> {
78    if let Expr::Lit(lit) = &name_value.value {
79        if let Lit::Bool(b) = &lit.lit {
80            return Ok(b.value);
81        }
82    }
83
84    Err(syn::Error::new(
85        name_value.value.span(),
86        format!("expected `{path} = false`", path = path_to_string(&name_value.path)),
87    ))
88}
89
90#[inline]
91pub(crate) fn meta_2_bool(meta: &Meta) -> syn::Result<bool> {
92    match &meta {
93        Meta::NameValue(name_value) => meta_name_value_2_bool(name_value),
94        Meta::List(list) => Ok(list.parse_args::<LitBool>()?.value),
95        Meta::Path(path) => Err(syn::Error::new(
96            path.span(),
97            format!("expected `{path} = false` or `{path}(false)`", path = path_to_string(path)),
98        )),
99    }
100}
101
102#[inline]
103pub(crate) fn meta_2_bool_allow_path(meta: &Meta) -> syn::Result<bool> {
104    match &meta {
105        Meta::Path(_) => Ok(true),
106        Meta::NameValue(name_value) => meta_name_value_2_bool(name_value),
107        Meta::List(list) => Ok(list.parse_args::<LitBool>()?.value),
108    }
109}
110
111#[inline]
112pub(crate) fn meta_name_value_2_ident_and_bool(
113    name_value: &MetaNameValue,
114) -> syn::Result<IdentOrBool> {
115    match &name_value.value {
116        Expr::Lit(lit) => match &lit.lit {
117            Lit::Str(lit) => match lit.parse::<Ident>() {
118                Ok(ident) => return Ok(IdentOrBool::Ident(ident)),
119                Err(_) if lit.value().is_empty() => {
120                    return Ok(IdentOrBool::Bool(false));
121                },
122                Err(error) => {
123                    return Err(error);
124                },
125            },
126            Lit::Bool(lit) => {
127                return Ok(IdentOrBool::Bool(lit.value));
128            },
129            _ => (),
130        },
131        Expr::Path(path) => {
132            if let Some(ident) = path.path.get_ident() {
133                return Ok(IdentOrBool::Ident(ident.clone()));
134            }
135        },
136        _ => (),
137    }
138
139    Err(syn::Error::new(
140        name_value.value.span(),
141        format!(
142            "expected `{path} = Ident` or `{path} = false`",
143            path = path_to_string(&name_value.path)
144        ),
145    ))
146}
147
148#[inline]
149pub(crate) fn meta_2_ident_and_bool(meta: &Meta) -> syn::Result<IdentOrBool> {
150    match &meta {
151        Meta::NameValue(name_value) => meta_name_value_2_ident_and_bool(name_value),
152        Meta::List(list) => list.parse_args::<IdentOrBool>(),
153        Meta::Path(path) => Err(syn::Error::new(
154            path.span(),
155            format!(
156                "expected `{path} = Ident`, `{path}(Ident)`, `{path} = false`, or `{path}(false)`",
157                path = path_to_string(path)
158            ),
159        )),
160    }
161}