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