darling_core/util/ident_string/
mod.rs

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