rocket_codegen/
name.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
use crate::http::uncased::UncasedStr;

use syn::{self, Ident, ext::IdentExt};
use proc_macro2::{Span, TokenStream};

/// A "name" read by codegen, which may or may not be an identifier. A `Name` is
/// typically constructed indirectly via FromMeta, or From<Ident> or directly
/// from a string via `Name::new()`. A name is tokenized as a string.
///
/// Some "names" in Rocket include:
///   * Dynamic parameter: `name` in `<name>`
///   * Renamed fields: `foo` in #[field(name = "foo")].
///
/// `Name` implements Hash, PartialEq, and Eq, and additionally PartialEq<S> for
/// all types `S: PartialEq<str>`. These implementations all compare the value
/// of `as_str()` only.
#[derive(Debug, Clone)]
pub struct Name {
    value: String,
    span: Span,
}

impl Name {
    /// Creates a new `Name` from the string `name` and span `span`. If
    /// `name` is a valid ident, the ident is stored as well.
    pub fn new<S: Into<String>>(name: S, span: Span) -> Self {
        Name { value: name.into(), span }
    }

    /// Returns the name as a string. Notably, if `self` was constructed from an
    /// Ident this method returns a name *without* an `r#` prefix.
    pub fn as_str(&self) -> &str {
        &self.value
    }

    /// Like `as_str()` but into an `&UncasedStr`.
    pub fn as_uncased_str(&self) -> &UncasedStr {
        UncasedStr::new(self.as_str())
    }

    pub fn span(&self) -> Span {
        self.span
    }
}

impl devise::FromMeta for Name {
    fn from_meta(meta: &devise::MetaItem) -> devise::Result<Self> {
        use devise::ext::SpanDiagnosticExt;

        if let syn::Lit::Str(s) = meta.lit()? {
            return Ok(Name::new(s.value(), s.span()));
        }

        Err(meta.value_span().error("invalid value: expected string literal"))
    }
}

impl quote::ToTokens for Name {
    fn to_tokens(&self, tokens: &mut TokenStream) {
        syn::LitStr::new(self.as_str(), self.span()).to_tokens(tokens)
    }
}

impl From<&Ident> for Name {
    fn from(ident: &Ident) -> Self {
        Name::new(ident.unraw().to_string(), ident.span())
    }
}

impl AsRef<str> for Name {
    fn as_ref(&self) -> &str {
        self.as_str()
    }
}

impl std::hash::Hash for Name {
    fn hash<H: std::hash::Hasher>(&self, hasher: &mut H) {
        self.as_str().hash(hasher)
    }
}

impl std::ops::Deref for Name {
    type Target = str;

    fn deref(&self) -> &Self::Target {
        self.as_str()
    }
}

impl Eq for Name { }

impl<S: PartialEq<str> + ?Sized> PartialEq<S> for Name {
    fn eq(&self, other: &S) -> bool {
        other == self.as_str()
    }
}

impl std::fmt::Display for Name {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        self.as_str().fmt(f)
    }
}