easy_macros_attributes/
internal.rs1use always_context::always_context;
7use anyhow::Context;
8use helpers::context;
9use lazy_static::lazy_static;
10use proc_macro2::TokenTree;
11use quote::ToTokens;
12use syn::{Ident, LitStr};
13
14lazy_static! {
15 pub(crate) static ref UNKNOWN: &'static str = "__unknown__";
16 pub(crate) static ref UNKNOWN_REGEX: regex::Regex =
17 regex::Regex::new(®ex::escape(*UNKNOWN)).expect("Failed to create regex for unknown");
18}
19
20#[derive(Debug)]
27pub struct AttrWithUnknown {
28 unknown_coordinate: usize,
31 unknown_group_coordinates: Vec<usize>,
32 partial_unknown_cords: PartialUnknownPos,
34 tokens_after_unknown: Vec<proc_macro2::TokenTree>,
38 before_unknown: String,
39 after_unknown: String,
40}
41
42#[derive(Debug)]
43struct PartialUnknownPos {
44 skip_start: usize,
45 skip_end: usize,
46}
47
48#[always_context]
49impl AttrWithUnknown {
50 pub fn new(attr: &syn::Attribute) -> anyhow::Result<Option<AttrWithUnknown>> {
51 let stream = attr.to_token_stream();
52 let string = stream.to_string();
53 if let Some(pos) = string.find(*UNKNOWN) {
54 let before_unknown = string.get(..pos)?.to_string();
56 let after_unknown = string.get(pos + UNKNOWN.len()..)?.to_string();
57
58 let mut unknown_group_coordinates = vec![];
61
62 struct DataRecursiveResult {
63 partial_unknown_cords: PartialUnknownPos,
64 tokens_after: Vec<proc_macro2::TokenTree>,
65 }
66
67 fn unknown_data_recursive(
70 token_stream: proc_macro2::TokenStream,
71 unknown_group_coordinates: &mut Vec<usize>,
72 ) -> Option<DataRecursiveResult> {
73 let mut tokens_after: Option<Vec<TokenTree>> = None;
74 let mut partial_unknown_cords = None;
75
76 for (index, token) in token_stream.into_iter().enumerate() {
77 if let Some(tokens_after) = &mut tokens_after {
78 tokens_after.push(token);
80 } else {
81 match token {
83 TokenTree::Group(group) => {
84 unknown_group_coordinates.push(index);
85 let result = unknown_data_recursive(
86 group.stream(),
87 unknown_group_coordinates,
88 );
89 if result.is_some() {
90 return result;
91 } else {
92 unknown_group_coordinates.pop();
93 }
94 }
95 TokenTree::Ident(ident) => {
96 let ident_str = ident.to_string();
97 let unknown_pos = UNKNOWN_REGEX.find(&ident_str);
98
99 if let Some(u_pos) = unknown_pos {
100 tokens_after = Some(vec![]);
102 unknown_group_coordinates.push(index);
103 partial_unknown_cords = Some(PartialUnknownPos {
104 skip_start: u_pos.start(),
105 skip_end: ident_str.len() - u_pos.end(),
106 });
107 }
108 }
109 TokenTree::Punct(_) => {}
110 TokenTree::Literal(literal) => {
111 let literal_str = literal.to_string();
112 let unknown_pos = UNKNOWN_REGEX.find(&literal_str);
113
114 if let Some(u_pos) = unknown_pos {
115 tokens_after = Some(vec![]);
117 unknown_group_coordinates.push(index);
118 partial_unknown_cords = Some(PartialUnknownPos {
119 skip_start: u_pos.start(),
120 skip_end: literal_str.len() - u_pos.end(),
121 });
122 }
123 }
124 }
125 }
126 }
127
128 if let (Some(partial_unknown_cords), Some(tokens_after)) =
129 (partial_unknown_cords, tokens_after)
130 {
131 Some(DataRecursiveResult {
132 partial_unknown_cords,
133 tokens_after,
134 })
135 } else {
136 None
137 }
138 }
139
140 let data_recursive_result =
141 unknown_data_recursive(stream, &mut unknown_group_coordinates);
142
143 let (token_after, unknown_coordinate, partial_unknown_cords) = if let Some(
144 DataRecursiveResult {
145 partial_unknown_cords,
146 mut tokens_after,
147 },
148 ) =
149 data_recursive_result
150 {
151 tokens_after.reverse();
153 let unknown_coordinate = unknown_group_coordinates.pop().with_context(context!(
155 "No unknown coordinates, but tokens after are not None! | tokens_after: {:?}",
156 tokens_after
157 ))?;
158 (tokens_after, unknown_coordinate, partial_unknown_cords)
159 } else {
160 anyhow::bail!(
161 "Unknown not found in the attribute! Recursive call failed, but it shouldn't"
162 );
163 };
164
165 return Ok(Some(AttrWithUnknown {
166 before_unknown,
167 after_unknown,
168 unknown_coordinate,
169 tokens_after_unknown: token_after,
170 unknown_group_coordinates,
171 partial_unknown_cords,
172 }));
173 }
174 Ok(None)
175 }
176
177 pub fn get_unknown(
178 &self,
179 attr: &syn::Attribute,
180 ) -> anyhow::Result<Option<proc_macro2::TokenStream>> {
181 let attr_tokens = attr.to_token_stream();
183 let attr_str = attr_tokens.to_string();
184
185 if !(attr_str.starts_with(&self.before_unknown) && attr_str.ends_with(&self.after_unknown))
187 {
188 return Ok(None);
189 }
190
191 let mut current_tokens = attr_tokens;
192
193 for group_index in self.unknown_group_coordinates.iter() {
194 match current_tokens.into_iter().nth(*group_index) {
195 Some(TokenTree::Group(group)) => {
196 current_tokens = group.stream();
197 }
198 i => {
199 anyhow::bail!("Bad group index! Expected Group, got {i:?} | self: {self:?}");
200 }
201 }
202 }
203
204 let mut unknown_tokens = current_tokens
206 .into_iter()
207 .skip(self.unknown_coordinate)
208 .collect::<Vec<TokenTree>>();
209 let unknown_tokens_len = unknown_tokens.len();
210
211 if !self.tokens_after_unknown.is_empty() {
213 unknown_tokens.drain(unknown_tokens_len - self.tokens_after_unknown.len()..);
214 }
215
216 {
218 let partial_unknown_cords = &self.partial_unknown_cords;
219
220 if partial_unknown_cords.skip_start != 0 {
222 let mut remove_first = false;
223 match unknown_tokens.first_mut() {
224 Some(TokenTree::Ident(ident)) => {
225 let ident_str = ident.to_string();
226 let unknown_replacement = &ident_str[partial_unknown_cords.skip_start..];
227
228 if unknown_replacement.is_empty() {
229 remove_first = true;
230 } else {
231 *ident = Ident::new(unknown_replacement, ident.span());
232 }
233 }
234 Some(TokenTree::Literal(literal)) => {
235 let lit_str = syn::parse2::<LitStr>(proc_macro2::TokenStream::from(
236 TokenTree::Literal(literal.clone()),
237 ))?;
238 let literal_str = lit_str.value();
239 let unknown_replacement = &literal_str[partial_unknown_cords.skip_start..];
240
241 if unknown_replacement.is_empty() {
242 remove_first = true;
243 } else {
244 *literal = proc_macro2::Literal::string(unknown_replacement);
245 }
246 }
247 Some(i) => anyhow::bail!(
248 "Expected ident or literal (for removing text before unknown), got {i}"
249 ),
250 None => {
251 anyhow::bail!(
252 "Unknown tokens is empty while looking for partial unknown! | self: {self:?}"
253 );
254 }
255 }
256
257 if remove_first {
258 unknown_tokens.remove(0);
259 }
260 }
261 if partial_unknown_cords.skip_end != 0 {
263 let mut remove_last = false;
264 match unknown_tokens.last_mut() {
265 Some(TokenTree::Ident(ident)) => {
266 let ident_str = ident.to_string();
267 let unknown_replacement =
268 &ident_str[0..ident_str.len() - partial_unknown_cords.skip_end];
269
270 if unknown_replacement.is_empty() {
271 remove_last = true;
272 } else {
273 *ident = Ident::new(unknown_replacement, ident.span());
274 }
275 }
276 Some(TokenTree::Literal(literal)) => {
277 let lit_str = syn::parse2::<LitStr>(proc_macro2::TokenStream::from(
278 TokenTree::Literal(literal.clone()),
279 ))?;
280 let literal_str = lit_str.value();
281 let unknown_replacement =
282 &literal_str[0..literal_str.len() - partial_unknown_cords.skip_end];
283
284 if unknown_replacement.is_empty() {
285 remove_last = true;
286 } else {
287 *literal = proc_macro2::Literal::string(unknown_replacement);
288 }
289 }
290 Some(i) => anyhow::bail!(
291 "Expected ident or literal (for removing text after unknown), got {i}"
292 ),
293 None => {
294 anyhow::bail!(
295 "Unknown tokens is empty while looking for partial unknown! | self: {self:?}"
296 );
297 }
298 }
299
300 if remove_last {
301 unknown_tokens.pop();
302 }
303 }
304 }
305
306 Ok(Some(proc_macro2::TokenStream::from_iter(unknown_tokens)))
307 }
308}