cxxbridge_macro/syntax/
qualified.rs

1use syn::ext::IdentExt;
2use syn::parse::{Error, ParseStream, Result};
3use syn::{Ident, LitStr, Token};
4
5pub(crate) struct QualifiedName {
6    pub segments: Vec<Ident>,
7}
8
9impl QualifiedName {
10    pub(crate) fn parse_quoted(lit: &LitStr) -> Result<Self> {
11        if lit.value().is_empty() {
12            let segments = Vec::new();
13            Ok(QualifiedName { segments })
14        } else {
15            lit.parse_with(|input: ParseStream| {
16                let allow_raw = false;
17                parse_unquoted(input, allow_raw)
18            })
19        }
20    }
21
22    pub(crate) fn parse_unquoted(input: ParseStream) -> Result<Self> {
23        let allow_raw = true;
24        parse_unquoted(input, allow_raw)
25    }
26
27    pub(crate) fn parse_quoted_or_unquoted(input: ParseStream) -> Result<Self> {
28        if input.peek(LitStr) {
29            let lit: LitStr = input.parse()?;
30            Self::parse_quoted(&lit)
31        } else {
32            Self::parse_unquoted(input)
33        }
34    }
35}
36
37fn parse_unquoted(input: ParseStream, allow_raw: bool) -> Result<QualifiedName> {
38    let mut segments = Vec::new();
39    let mut trailing_punct = true;
40    let leading_colons: Option<Token![::]> = input.parse()?;
41    while trailing_punct && input.peek(Ident::peek_any) {
42        let mut ident = Ident::parse_any(input)?;
43        if let Some(unraw) = ident.to_string().strip_prefix("r#") {
44            if !allow_raw {
45                let msg = format!(
46                    "raw identifier `{}` is not allowed in a quoted namespace; use `{}`, or remove quotes",
47                    ident, unraw,
48                );
49                return Err(Error::new(ident.span(), msg));
50            }
51            ident = Ident::new(unraw, ident.span());
52        }
53        segments.push(ident);
54        let colons: Option<Token![::]> = input.parse()?;
55        trailing_punct = colons.is_some();
56    }
57    if segments.is_empty() && leading_colons.is_none() {
58        return Err(input.error("expected path"));
59    } else if trailing_punct {
60        return Err(input.error("expected path segment"));
61    }
62    Ok(QualifiedName { segments })
63}