bevy_reflect_derive/
type_path.rs

1use proc_macro2::Ident;
2use syn::{
3    parenthesized,
4    parse::{Parse, ParseStream},
5    token::Paren,
6    Generics, Path, PathSegment, Token,
7};
8
9pub(crate) fn parse_path_no_leading_colon(input: ParseStream) -> syn::Result<Path> {
10    if input.peek(Token![::]) {
11        return Err(input.error("did not expect a leading double colon (`::`)"));
12    }
13
14    let path = Path::parse_mod_style(input)?;
15
16    if path.segments.is_empty() {
17        Err(input.error("expected a path"))
18    } else {
19        Ok(path)
20    }
21}
22
23/// An alias for a `TypePath`.
24///
25/// This is the parenthesized part of `(in my_crate::foo as MyType) SomeType`.
26pub(crate) struct CustomPathDef {
27    path: Path,
28    name: Option<Ident>,
29}
30
31impl CustomPathDef {
32    pub fn into_path(mut self, default_name: &Ident) -> Path {
33        let name = PathSegment::from(self.name.unwrap_or_else(|| default_name.clone()));
34        self.path.segments.push(name);
35        self.path
36    }
37
38    pub fn parse_parenthesized(input: ParseStream) -> syn::Result<Option<Self>> {
39        if input.peek(Paren) {
40            let path;
41            parenthesized!(path in input);
42            Ok(Some(path.call(Self::parse)?))
43        } else {
44            Ok(None)
45        }
46    }
47
48    fn parse(input: ParseStream) -> syn::Result<Self> {
49        input.parse::<Token![in]>()?;
50
51        let custom_path = parse_path_no_leading_colon(input)?;
52
53        if !input.peek(Token![as]) {
54            return Ok(Self {
55                path: custom_path,
56                name: None,
57            });
58        }
59
60        input.parse::<Token![as]>()?;
61        let custom_name: Ident = input.parse()?;
62
63        Ok(Self {
64            path: custom_path,
65            name: Some(custom_name),
66        })
67    }
68}
69
70pub(crate) enum NamedTypePathDef {
71    External {
72        path: Path,
73        generics: Generics,
74        custom_path: Option<CustomPathDef>,
75    },
76    Primitive(Ident),
77}
78
79impl Parse for NamedTypePathDef {
80    fn parse(input: ParseStream) -> syn::Result<Self> {
81        let custom_path = CustomPathDef::parse_parenthesized(input)?;
82
83        let path = Path::parse_mod_style(input)?;
84        let mut generics = input.parse::<Generics>()?;
85        generics.where_clause = input.parse()?;
86
87        if path.leading_colon.is_none() && custom_path.is_none() {
88            if path.segments.len() == 1 {
89                let ident = path.segments.into_iter().next().unwrap().ident;
90                Ok(NamedTypePathDef::Primitive(ident))
91            } else {
92                Err(input.error("non-customized paths must start with a double colon (`::`)"))
93            }
94        } else {
95            Ok(NamedTypePathDef::External {
96                path,
97                generics,
98                custom_path,
99            })
100        }
101    }
102}