Rocket/core/codegen/src/name.rs

103 lines
2.7 KiB
Rust

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)
}
}