• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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