use std::fmt; use std::hash::{Hash, Hasher}; use proc_macro2::{Span, TokenStream}; use quote::ToTokens; use syn::{Ident, Meta}; use crate::{FromMeta, Result}; /// A wrapper for an `Ident` which also keeps the value as a string. /// /// This struct can be used to perform string comparisons and operations. #[derive(Clone, PartialOrd, Ord)] pub struct IdentString { ident: Ident, string: String, } impl IdentString { /// Create a new `IdentString`. pub fn new(ident: Ident) -> Self { IdentString { string: ident.to_string(), ident, } } /// Get the ident as a `proc_macro2::Ident`. pub fn as_ident(&self) -> &Ident { &self.ident } /// Get the ident as a string. pub fn as_str(&self) -> &str { &self.string } /// Get the location of this `Ident` in source. pub fn span(&self) -> Span { self.ident.span() } /// Apply some transform to the ident's string representation. /// /// # Panics /// This will panic if the transform produces an invalid ident. pub fn map(self, map_fn: F) -> Self where F: FnOnce(String) -> S, S: AsRef, { let span = self.span(); let string = map_fn(self.string); Ident::new(string.as_ref(), span).into() } } impl AsRef for IdentString { fn as_ref(&self) -> &Ident { self.as_ident() } } impl AsRef for IdentString { fn as_ref(&self) -> &str { self.as_str() } } impl From for IdentString { fn from(ident: Ident) -> Self { IdentString::new(ident) } } impl From for Ident { fn from(v: IdentString) -> Ident { v.ident } } impl From for String { fn from(v: IdentString) -> String { v.string } } impl Eq for IdentString {} impl PartialEq for IdentString { fn eq(&self, rhs: &Self) -> bool { self.ident == rhs.ident } } impl PartialEq for IdentString { fn eq(&self, rhs: &String) -> bool { self.as_str() == rhs } } impl<'a> PartialEq<&'a str> for IdentString { fn eq(&self, rhs: &&str) -> bool { self.as_str() == *rhs } } impl Hash for IdentString { fn hash(&self, state: &mut H) { self.ident.hash(state); } } impl ToTokens for IdentString { fn to_tokens(&self, tokens: &mut TokenStream) { self.ident.to_tokens(tokens); } } impl fmt::Debug for IdentString { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{:?}", self.ident) } } impl fmt::Display for IdentString { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}", self.ident) } } impl FromMeta for IdentString { fn from_meta(item: &Meta) -> Result { Ident::from_meta(item).map(IdentString::from) } } #[cfg(test)] mod tests { use syn::parse_quote; use super::IdentString; #[test] fn convert() { let i_str = IdentString::new(parse_quote!(t)); assert_eq!(i_str.as_str(), "t"); } #[test] fn map_transform() { let i = IdentString::new(parse_quote!(my)); let after = i.map(|v| format!("var_{}", v)); assert_eq!(after, "var_my"); assert_eq!(after, String::from("var_my")); } }