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