• 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_quoted(lit: &LitStr) -> Result<Self>10     pub 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 
parse_unquoted(input: ParseStream) -> Result<Self>22     pub fn parse_unquoted(input: ParseStream) -> Result<Self> {
23         let allow_raw = true;
24         parse_unquoted(input, allow_raw)
25     }
26 
parse_quoted_or_unquoted(input: ParseStream) -> Result<Self>27     pub 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 
parse_unquoted(input: ParseStream, allow_raw: bool) -> Result<QualifiedName>37 fn 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 }
64