1use std::collections::HashMap;
64
65use macro_string::MacroString;
66use proc_macro::TokenStream;
67use proc_macro2::{Group, Ident, TokenStream as TokenStream2, TokenTree};
68use quote::{ToTokens, TokenStreamExt};
69use syn::{
70 Token,
71 parse::{Parse, ParseStream},
72 parse_macro_input,
73 punctuated::Punctuated,
74};
75
76enum Value {
77 MacroString(MacroString),
78 None,
79}
80
81impl Value {
82 pub fn to_string(&self) -> Option<String> {
83 match self {
84 Value::MacroString(MacroString(n)) => Some(n.clone()),
85 Value::None => None,
86 }
87 }
88}
89
90impl Parse for Value {
91 fn parse(input: ParseStream) -> syn::Result<Self> {
92 if input
93 .step(|x| {
94 if let Some((ident, cursor)) = x.ident()
95 && ident == "None"
96 {
97 Ok((ident, cursor))
98 } else {
99 Err(x.error("Expected string or None"))
100 }
101 })
102 .is_ok()
103 {
104 Ok(Value::None)
105 } else {
106 Ok(Value::MacroString(input.parse()?))
107 }
108 }
109}
110
111struct Decl {
112 _hash: Token![#],
113 ident: Ident,
114 _eq_token: Token![=],
115 value: Value,
116}
117
118impl Decl {
119 fn name_to_tokens(&self) -> TokenStream2 {
120 let mut tokens = TokenStream2::new();
121 self._hash.to_tokens(&mut tokens);
122 self.ident.to_tokens(&mut tokens);
123 tokens
124 }
125}
126
127impl Parse for Decl {
128 fn parse(input: ParseStream) -> syn::Result<Self> {
129 let hash = input.parse()?;
130 let ident = input.parse()?;
131 let eq_token = input.parse()?;
132 let value = input.parse()?;
133 Ok(Self {
134 _hash: hash,
135 ident,
136 _eq_token: eq_token,
137 value,
138 })
139 }
140}
141
142struct Decls {
143 decls: Punctuated<Decl, Token![,]>,
144 _arrow: Token![=>],
145 body: TokenStream2,
146}
147
148impl Parse for Decls {
149 fn parse(input: ParseStream) -> syn::Result<Self> {
150 Ok(Self {
151 decls: Punctuated::parse_separated_nonempty(input)?,
152 _arrow: input.parse()?,
153 body: {
156 if input.peek(syn::token::Brace) {
157 let g: Group = input.parse()?;
158 g.stream()
159 } else {
160 input.parse()?
161 }
162 },
163 })
164 }
165}
166
167fn append_error(errors: &mut Option<syn::Error>, new: syn::Error) {
168 if let Some(errors) = errors {
169 errors.combine(new);
170 } else {
171 *errors = Some(new);
172 }
173}
174
175fn translate_stream(
176 stream: TokenStream2,
177 map: &HashMap<String, Decl>,
178 errors: &mut Option<syn::Error>,
179) -> TokenStream2 {
180 let mut out = TokenStream2::new();
181 let mut iter = stream.into_iter().peekable();
182 while let Some(tok) = iter.next() {
183 match tok {
184 TokenTree::Ident(_) => out.append(tok),
185 TokenTree::Group(group) => {
186 let mut group = Group::new(
187 group.delimiter(),
188 translate_stream(group.stream(), map, errors),
189 );
190 group.set_span(group.span());
191 out.append(TokenTree::Group(group));
192 }
193 TokenTree::Punct(ref p) if p.as_char() == '#' => {
194 if let Some(TokenTree::Ident(_)) = iter.peek() {
195 let Some(TokenTree::Ident(ident)) = iter.next() else {
196 unreachable!();
197 };
198 let strident = ident.to_string();
199 if let Some(decl) = map.get(&strident) {
200 let ident = if let Some(value) = &decl.value.to_string() {
201 Ident::new(value, ident.span()) } else {
203 out.append(tok);
204 ident
205 };
206 out.append(TokenTree::Ident(ident));
207 } else {
208 let mut tokens = TokenStream2::new();
209 tokens.append(tok);
210 tokens.append(ident);
211 append_error(
212 errors,
213 syn::Error::new_spanned(
215 tokens,
216 format!(
217 "Unknown ident variable. If you intended to literally use '#{0}', add `#{0} = None` to the declarations",
218 strident
219 ),
220 ),
221 );
222 continue;
223 }
224 } else {
225 out.append(tok);
226 }
227 }
228 TokenTree::Punct(_) | TokenTree::Literal(_) => out.append(tok),
229 }
230 }
231
232 out
233}
234
235#[proc_macro]
255pub fn ident_str(input: TokenStream) -> TokenStream {
256 let decls = parse_macro_input!(input as Decls);
257 let mut map = HashMap::<String, Decl>::with_capacity(decls.decls.len());
258 let mut errors: Option<syn::Error> = None;
259 let mut can_continue = true;
260 for d in decls.decls.into_iter() {
261 let strident = d.ident.to_string();
262 let existing = map.get(&strident);
263 if existing.is_some() {
264 append_error(
265 &mut errors,
266 syn::Error::new_spanned(
267 d.name_to_tokens(),
268 format!("Redefinition of #{}", d.ident),
269 ),
270 );
271 }
272
273 if let Some(valstring) = d.value.to_string()
274 && syn::parse_str::<Ident>(&valstring).is_err()
275 {
276 append_error(
277 &mut errors,
278 syn::Error::new_spanned(
279 d.name_to_tokens(),
280 format!("Invalid identifier: {:?}", valstring),
281 ),
282 );
283 can_continue = false;
284 }
285 map.insert(strident, d);
286 }
287
288 let mut tokens = if can_continue {
289 translate_stream(decls.body, &map, &mut errors)
290 } else {
291 debug_assert!(errors.is_some());
292 TokenStream2::new()
293 };
294
295 if let Some(errors) = errors {
296 errors.to_compile_error().to_tokens(&mut tokens);
297 }
298
299 tokens.into()
300}