rocket_codegen/
name.rs

1use crate::http::uncased::UncasedStr;
2
3use syn::{self, Ident, ext::IdentExt};
4use proc_macro2::{Span, TokenStream};
5
6/// A "name" read by codegen, which may or may not be an identifier. A `Name` is
7/// typically constructed indirectly via FromMeta, or From<Ident> or directly
8/// from a string via `Name::new()`. A name is tokenized as a string.
9///
10/// Some "names" in Rocket include:
11///   * Dynamic parameter: `name` in `<name>`
12///   * Renamed fields: `foo` in #[field(name = "foo")].
13///
14/// `Name` implements Hash, PartialEq, and Eq, and additionally PartialEq<S> for
15/// all types `S: PartialEq<str>`. These implementations all compare the value
16/// of `as_str()` only.
17#[derive(Debug, Clone)]
18pub struct Name {
19    value: String,
20    span: Span,
21}
22
23impl Name {
24    /// Creates a new `Name` from the string `name` and span `span`. If
25    /// `name` is a valid ident, the ident is stored as well.
26    pub fn new<S: Into<String>>(name: S, span: Span) -> Self {
27        Name { value: name.into(), span }
28    }
29
30    /// Returns the name as a string. Notably, if `self` was constructed from an
31    /// Ident this method returns a name *without* an `r#` prefix.
32    pub fn as_str(&self) -> &str {
33        &self.value
34    }
35
36    /// Like `as_str()` but into an `&UncasedStr`.
37    pub fn as_uncased_str(&self) -> &UncasedStr {
38        UncasedStr::new(self.as_str())
39    }
40
41    pub fn span(&self) -> Span {
42        self.span
43    }
44}
45
46impl devise::FromMeta for Name {
47    fn from_meta(meta: &devise::MetaItem) -> devise::Result<Self> {
48        use devise::ext::SpanDiagnosticExt;
49
50        if let syn::Lit::Str(s) = meta.lit()? {
51            return Ok(Name::new(s.value(), s.span()));
52        }
53
54        Err(meta.value_span().error("invalid value: expected string literal"))
55    }
56}
57
58impl quote::ToTokens for Name {
59    fn to_tokens(&self, tokens: &mut TokenStream) {
60        syn::LitStr::new(self.as_str(), self.span()).to_tokens(tokens)
61    }
62}
63
64impl From<&Ident> for Name {
65    fn from(ident: &Ident) -> Self {
66        Name::new(ident.unraw().to_string(), ident.span())
67    }
68}
69
70impl AsRef<str> for Name {
71    fn as_ref(&self) -> &str {
72        self.as_str()
73    }
74}
75
76impl std::hash::Hash for Name {
77    fn hash<H: std::hash::Hasher>(&self, hasher: &mut H) {
78        self.as_str().hash(hasher)
79    }
80}
81
82impl std::ops::Deref for Name {
83    type Target = str;
84
85    fn deref(&self) -> &Self::Target {
86        self.as_str()
87    }
88}
89
90impl Eq for Name { }
91
92impl<S: PartialEq<str> + ?Sized> PartialEq<S> for Name {
93    fn eq(&self, other: &S) -> bool {
94        other == self.as_str()
95    }
96}
97
98impl std::fmt::Display for Name {
99    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
100        self.as_str().fmt(f)
101    }
102}