bpaf_derive/
field.rs

1use syn::{PathArguments, Type};
2
3pub(crate) use crate::named_field::StructField;
4
5#[derive(PartialEq, Debug, Clone)]
6pub(crate) enum Shape {
7    /// Option<T>
8    Optional(Type),
9    /// Vec<T>,
10    Multiple(Type),
11    /// bool
12    Bool,
13    /// ()
14    Unit,
15    /// T
16    Direct(Type),
17}
18
19pub(crate) fn split_type(ty: &Type) -> Shape {
20    fn single_arg(x: &PathArguments) -> Option<Type> {
21        match x {
22            PathArguments::AngleBracketed(arg) => {
23                if arg.args.len() == 1 {
24                    match &arg.args[0] {
25                        syn::GenericArgument::Type(ty) => Some(ty.clone()),
26                        _ => None,
27                    }
28                } else {
29                    None
30                }
31            }
32            PathArguments::None | PathArguments::Parenthesized(_) => None,
33        }
34    }
35
36    fn try_split_type(ty: &Type) -> Option<Shape> {
37        if let Type::Tuple(syn::TypeTuple { elems, .. }) = ty {
38            if elems.is_empty() {
39                return Some(Shape::Unit);
40            }
41        }
42
43        let last = match ty {
44            Type::Path(p) => p.path.segments.last()?,
45            _ => return None,
46        };
47        if last.ident == "Vec" {
48            Some(Shape::Multiple(single_arg(&last.arguments)?))
49        } else if last.ident == "Option" {
50            Some(Shape::Optional(single_arg(&last.arguments)?))
51        } else if last.ident == "bool" {
52            Some(Shape::Bool)
53        } else {
54            None
55        }
56    }
57    try_split_type(ty).unwrap_or_else(|| Shape::Direct(ty.clone()))
58}