1use std::fmt;
2use std::hash::{Hash, Hasher};
3
4use proc_macro2::{Span, TokenStream};
5use quote::ToTokens;
6use syn::{Ident, Meta};
7
8use 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)]
14pub struct IdentString {
15 ident: Ident,
16 string: String,
17}
18
19impl IdentString {
20 /// Create a new `IdentString`.
21 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`.
29 pub fn as_ident(&self) -> &Ident {
30 &self.ident
31 }
32
33 /// Get the ident as a string.
34 pub fn as_str(&self) -> &str {
35 &self.string
36 }
37
38 /// Get the location of this `Ident` in source.
39 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.
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
58impl AsRef<Ident> for IdentString {
59 fn as_ref(&self) -> &Ident {
60 self.as_ident()
61 }
62}
63
64impl AsRef<str> for IdentString {
65 fn as_ref(&self) -> &str {
66 self.as_str()
67 }
68}
69
70impl From<Ident> for IdentString {
71 fn from(ident: Ident) -> Self {
72 IdentString::new(ident)
73 }
74}
75
76impl From<IdentString> for Ident {
77 fn from(v: IdentString) -> Ident {
78 v.ident
79 }
80}
81
82impl From<IdentString> for String {
83 fn from(v: IdentString) -> String {
84 v.string
85 }
86}
87
88impl Eq for IdentString {}
89
90impl PartialEq for IdentString {
91 fn eq(&self, rhs: &Self) -> bool {
92 self.ident == rhs.ident
93 }
94}
95
96impl PartialEq<String> for IdentString {
97 fn eq(&self, rhs: &String) -> bool {
98 self.as_str() == rhs
99 }
100}
101
102impl<'a> PartialEq<&'a str> for IdentString {
103 fn eq(&self, rhs: &&str) -> bool {
104 self.as_str() == *rhs
105 }
106}
107
108impl Hash for IdentString {
109 fn hash<H: Hasher>(&self, state: &mut H) {
110 self.ident.hash(state);
111 }
112}
113
114impl ToTokens for IdentString {
115 fn to_tokens(&self, tokens: &mut TokenStream) {
116 self.ident.to_tokens(tokens);
117 }
118}
119
120impl fmt::Debug for IdentString {
121 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
122 write!(f, "{:?}", self.ident)
123 }
124}
125
126impl fmt::Display for IdentString {
127 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
128 write!(f, "{}", self.ident)
129 }
130}
131
132impl FromMeta for IdentString {
133 fn from_meta(item: &Meta) -> Result<Self> {
134 Ident::from_meta(item).map(op:IdentString::from)
135 }
136}
137
138#[cfg(test)]
139mod tests {
140 use syn::parse_quote;
141
142 use super::IdentString;
143
144 #[test]
145 fn convert() {
146 let i_str = IdentString::new(parse_quote!(t));
147 assert_eq!(i_str.as_str(), "t");
148 }
149
150 #[test]
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