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