1use super::{ToTokens, TokenStream};
2use crate::lex::{Cursor, LexError, Scan};
3use crate::{Span, TokenTree};
4
5#[macro_export]
44macro_rules! ident {
45 ($x:ident) => { $crate::Ident::lex(stringify!($x).to_string()).expect("invalid syntax") };
46 ($x:expr) => { $crate::Ident::lex($x).expect("invalid syntax") };
47 ($head:ident, $($tail:tt)+) => {{
48 let mut __ident = stringify!($head).to_string();
49 ident!(@accum __ident, $($tail)+)
50 }};
51 ($head:expr, $($tail:tt)+) => {{
52 let mut __ident = $head.to_string();
53 ident!(@accum __ident, $($tail)+)
54 }};
55 (@accum $acc:ident, $next:ident, $($tail:tt)+) => {{
56 $acc += stringify!($next);
57 ident!(@accum $acc, $($tail)+)
58 }};
59 (@accum $acc:ident, $next:expr, $($tail:tt)+) => {{
60 $acc += &$next.to_string();
61 ident!(@accum $acc, $($tail)+)
62 }};
63 (@accum $acc:ident, $last:ident) => {{
64 $acc += stringify!($last);
65 $crate::Ident::lex($acc).expect("invalid syntax")
66 }};
67 (@accum $acc:ident, $last:expr) => {{
68 $acc += &$last.to_string();
69 $crate::Ident::lex($acc).expect("invalid syntax")
70 }};
71}
72
73#[derive(Debug, Clone, PartialEq, Eq)]
75pub struct Ident {
76 text: Box<str>,
77 span: Span,
78}
79
80impl Ident {
81 #[inline]
82 pub fn new(text: impl std::fmt::Display) -> Self {
83 Self {
84 text: text.to_string().into_boxed_str(),
85 span: Span::default(),
86 }
87 }
88
89 #[inline]
90 pub fn lex(input: impl std::fmt::Display) -> Result<Self, LexError> {
91 std::str::FromStr::from_str(&input.to_string())
92 }
93
94 #[inline]
95 pub fn with_span(mut self, span: Span) -> Self {
96 self.span = span;
97 self
98 }
99
100 #[inline]
101 pub fn text(&self) -> &str {
102 match self.text.strip_prefix("r#") {
103 Some(rest) => rest,
104 None => &self.text,
105 }
106 }
107
108 #[inline]
109 pub fn is_raw(&self) -> bool {
110 self.text.starts_with("r#")
111 }
112
113 #[inline]
114 pub fn span(&self) -> Span {
115 self.span
116 }
117
118 #[inline]
119 pub fn set_span(&mut self, span: Span) {
120 self.span = span;
121 }
122
123 #[inline]
124 pub fn to_lowercase(mut self) -> Self {
125 self.text = self.text.to_lowercase().into_boxed_str();
126 self
127 }
128
129 #[inline]
130 pub fn to_uppercase(mut self) -> Self {
131 self.text = self.text.to_uppercase().into_boxed_str();
132 self
133 }
134
135 #[inline]
136 pub fn to_token_tree(&self) -> TokenTree {
137 TokenTree::Ident(self.clone())
138 }
139
140 #[inline]
141 pub fn into_token_tree(self) -> TokenTree {
142 TokenTree::Ident(self)
143 }
144}
145
146impl Scan for Ident {
147 fn scan(cursor: Cursor<'_>) -> Result<(Cursor<'_>, Self), LexError> {
148 if cursor.starts_with("r#") {
150 let after = cursor.advance_by(2);
151 let end = after.skip_while(unicode_ident::is_xid_continue);
152
153 if end.offset() == after.offset() {
154 return cursor.error().into();
155 }
156
157 let span = cursor.span_to(&end);
158 let name = &cursor.rest()[..end.offset() as usize - cursor.offset() as usize];
159 return Ok((end, Self::new(name).with_span(span)));
160 }
161
162 let first = cursor.first().ok_or(cursor.error())?;
163
164 if first != '_' && !unicode_ident::is_xid_start(first) {
165 return cursor.error().into();
166 }
167
168 let end = cursor.advance_by(first.len_utf8()).skip_while(unicode_ident::is_xid_continue);
169 let span = cursor.span_to(&end);
170 let text = &cursor.rest()[..end.offset() as usize - cursor.offset() as usize];
171 Ok((end, Self::new(text).with_span(span)))
172 }
173}
174
175impl std::fmt::Display for Ident {
176 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
177 write!(f, "{}", self.text)
178 }
179}
180
181impl ToTokens for Ident {
182 fn to_tokens(&self, tokens: &mut TokenStream) {
183 tokens.extend_one(TokenTree::Ident(self.clone()));
184 }
185}
186
187impl crate::Spanner for Ident {
188 fn span(&self) -> Span {
189 self.span
190 }
191}
192
193impl std::str::FromStr for Ident {
194 type Err = LexError;
195
196 fn from_str(s: &str) -> Result<Self, Self::Err> {
197 let cursor = Cursor::new(s, 0);
198 let (_, ident) = Self::scan(cursor)?;
199 Ok(ident)
200 }
201}
202
203#[cfg(feature = "serde")]
204impl serde::Serialize for Ident {
205 fn serialize<S>(&self, s: S) -> Result<S::Ok, S::Error>
206 where
207 S: serde::Serializer,
208 {
209 self.text.serialize(s)
210 }
211}
212
213impl PartialEq<str> for Ident {
214 fn eq(&self, other: &str) -> bool {
215 self.text.as_ref() == other
216 }
217}
218
219impl PartialEq<&str> for Ident {
220 fn eq(&self, other: &&str) -> bool {
221 self.text.as_ref() == *other
222 }
223}
224
225impl std::hash::Hash for Ident {
226 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
227 self.text.hash(state);
228 }
229}