1 use std::fmt; 2 use std::hash::{Hash, Hasher}; 3 4 use proc_macro2::{Span, TokenStream}; 5 use quote::ToTokens; 6 use syn::{Ident, Meta}; 7 8 use crate::{FromMeta, Result}; 9 10 /// A wrapper for an `Ident` which also keeps the value as a string. 11 /// 12 /// This struct can be used to perform string comparisons and operations. 13 #[derive(Clone, PartialOrd, Ord)] 14 pub struct IdentString { 15 ident: Ident, 16 string: String, 17 } 18 19 impl IdentString { 20 /// Create a new `IdentString`. new(ident: Ident) -> Self21 pub fn new(ident: Ident) -> Self { 22 IdentString { 23 string: ident.to_string(), 24 ident, 25 } 26 } 27 28 /// Get the ident as a `proc_macro2::Ident`. as_ident(&self) -> &Ident29 pub fn as_ident(&self) -> &Ident { 30 &self.ident 31 } 32 33 /// Get the ident as a string. as_str(&self) -> &str34 pub fn as_str(&self) -> &str { 35 &self.string 36 } 37 38 /// Get the location of this `Ident` in source. span(&self) -> Span39 pub fn span(&self) -> Span { 40 self.ident.span() 41 } 42 43 /// Apply some transform to the ident's string representation. 44 /// 45 /// # Panics 46 /// This will panic if the transform produces an invalid ident. map<F, S>(self, map_fn: F) -> Self where F: FnOnce(String) -> S, S: AsRef<str>,47 pub fn map<F, S>(self, map_fn: F) -> Self 48 where 49 F: FnOnce(String) -> S, 50 S: AsRef<str>, 51 { 52 let span = self.span(); 53 let string = map_fn(self.string); 54 Ident::new(string.as_ref(), span).into() 55 } 56 } 57 58 impl AsRef<Ident> for IdentString { as_ref(&self) -> &Ident59 fn as_ref(&self) -> &Ident { 60 self.as_ident() 61 } 62 } 63 64 impl AsRef<str> for IdentString { as_ref(&self) -> &str65 fn as_ref(&self) -> &str { 66 self.as_str() 67 } 68 } 69 70 impl From<Ident> for IdentString { from(ident: Ident) -> Self71 fn from(ident: Ident) -> Self { 72 IdentString::new(ident) 73 } 74 } 75 76 impl From<IdentString> for Ident { from(v: IdentString) -> Ident77 fn from(v: IdentString) -> Ident { 78 v.ident 79 } 80 } 81 82 impl From<IdentString> for String { from(v: IdentString) -> String83 fn from(v: IdentString) -> String { 84 v.string 85 } 86 } 87 88 impl Eq for IdentString {} 89 90 impl PartialEq for IdentString { eq(&self, rhs: &Self) -> bool91 fn eq(&self, rhs: &Self) -> bool { 92 self.ident == rhs.ident 93 } 94 } 95 96 impl PartialEq<String> for IdentString { eq(&self, rhs: &String) -> bool97 fn eq(&self, rhs: &String) -> bool { 98 self.as_str() == rhs 99 } 100 } 101 102 impl<'a> PartialEq<&'a str> for IdentString { eq(&self, rhs: &&str) -> bool103 fn eq(&self, rhs: &&str) -> bool { 104 self.as_str() == *rhs 105 } 106 } 107 108 impl Hash for IdentString { hash<H: Hasher>(&self, state: &mut H)109 fn hash<H: Hasher>(&self, state: &mut H) { 110 self.ident.hash(state); 111 } 112 } 113 114 impl ToTokens for IdentString { to_tokens(&self, tokens: &mut TokenStream)115 fn to_tokens(&self, tokens: &mut TokenStream) { 116 self.ident.to_tokens(tokens); 117 } 118 } 119 120 impl fmt::Debug for IdentString { fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result121 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 122 write!(f, "{:?}", self.ident) 123 } 124 } 125 126 impl fmt::Display for IdentString { fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result127 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 128 write!(f, "{}", self.ident) 129 } 130 } 131 132 impl FromMeta for IdentString { from_meta(item: &Meta) -> Result<Self>133 fn from_meta(item: &Meta) -> Result<Self> { 134 Ident::from_meta(item).map(IdentString::from) 135 } 136 } 137 138 #[cfg(test)] 139 mod tests { 140 use syn::parse_quote; 141 142 use super::IdentString; 143 144 #[test] convert()145 fn convert() { 146 let i_str = IdentString::new(parse_quote!(t)); 147 assert_eq!(i_str.as_str(), "t"); 148 } 149 150 #[test] map_transform()151 fn map_transform() { 152 let i = IdentString::new(parse_quote!(my)); 153 let after = i.map(|v| format!("var_{}", v)); 154 assert_eq!(after, "var_my"); 155 assert_eq!(after, String::from("var_my")); 156 } 157 } 158