macro_tools/diag.rs
1//!
2//! Macro helpers.
3//!
4
5/// Define a private namespace for all its items.
6mod private
7{
8
9 /// Adds indentation and optional prefix/postfix to each line of the given string.
10 ///
11 /// This function iterates over each line in the input string and applies the specified
12 /// prefix and postfix to it, effectively indenting the string and optionally wrapping
13 /// each line with additional content.
14 ///
15 /// # Parameters
16 /// - `prefix` : The string to prepend to each line, typically used for indentation.
17 /// - `src` : The source string to be indented and modified.
18 /// - `postfix` : The string to append to each line, can be used for line terminators or other suffixes.
19 ///
20 /// # Type Parameters
21 /// - `Prefix` : A type that can be referenced as a string slice, for the prefix.
22 /// - `Src` : A type that can be referenced as a string slice, for the source string.
23 /// - `Postfix` : A type that can be referenced as a string slice, for the postfix.
24 ///
25 /// # Returns
26 /// A `String` that represents the original `src` string with `prefix` and `postfix` applied to each line.
27 ///
28 /// # Example
29 /// ```
30 /// use macro_tools ::diag;
31 ///
32 /// let input = "Line 1\nLine 2\nLine 3";
33 /// let indented = diag ::indentation( " ", input, ";" );
34 /// assert_eq!( indented, " Line 1;\n Line 2;\n Line 3;" );
35 ///
36 /// // Demonstrating the function's handling of trailing newlines
37 /// let input_with_newline = "Line 1\nLine 2\nLine 3\n";
38 /// let indented_with_newline = diag ::indentation( " ", input_with_newline, ";" );
39 /// assert_eq!( indented_with_newline, " Line 1;\n Line 2;\n Line 3;\n ;" );
40 /// ```
41 ///
42 /// In the example above, `indentation` is used to add two spaces before each line
43 /// and a semicolon at the end of each line. The function also demonstrates handling
44 /// of input strings that end with a newline character by appending an additional line
45 /// consisting only of the prefix and postfix.
46 pub fn indentation< Prefix, Src, Postfix >(prefix: Prefix, src: Src, postfix: Postfix) -> String
47 where
48 Prefix: AsRef< str >,
49 Src: AsRef< str >,
50 Postfix: AsRef< str >,
51 {
52 let prefix = prefix.as_ref();
53 let postfix = postfix.as_ref();
54 let src = src.as_ref();
55
56 let mut result = src.lines().enumerate().fold(String ::new(), |mut a, b| {
57 if b.0 > 0
58 {
59 a.push('\n');
60 }
61 a.push_str(prefix);
62 a.push_str(b.1);
63 a.push_str(postfix);
64 a
65 });
66
67 if src.ends_with('\n') || src.ends_with("\n\r") || src.ends_with("\r\n")
68 {
69 result.push('\n');
70 result.push_str(prefix);
71 result.push_str(postfix);
72 }
73
74 result
75 }
76
77 /// Formats a debugging report for code transformation processes, detailing both the original and generated code for easy comparison and review.
78 ///
79 /// This function creates a structured report comprising the initial input code, the resulting generated code, and an explanatory context. It is designed to facilitate debugging and documentation of code transformations, such as those performed in procedural macros or similar code generation tasks. The report categorizes the information into labeled sections to enhance readability and traceability.
80 ///
81 /// This function helps visualize the changes from the original to the generated code, assisting developers in verifying and understanding the transformations applied during code generation processes.
82 ///
83 /// # Parameters
84 ///
85 /// - `about` : A description or context explaining the purpose or nature of the transformation. This information is displayed at the beginning of the report to provide an overview of the code transformation context.
86 /// - `input` : The original code before transformation. This is typically the code that is subject to processing by macros or other code generation tools.
87 /// - `output` : The code generated as a result of the transformation. This reflects the changes or enhancements made to the original code.
88 ///
89 /// # Type Parameters
90 ///
91 /// - `IntoAbout` : A type that can be converted into a string representation, providing a descriptive context for the report.
92 /// - `IntoInput` : A type representing the original code, which can be converted into a string format for display.
93 /// - `IntoOutput` : A type representing the generated code, which can be converted into a string format for display.
94 ///
95 /// # Returns
96 ///
97 /// A string containing the formatted debug report, organized into sections with appropriate labels and indentation to distinguish between the original and generated code segments.
98 ///
99 /// # Examples
100 ///
101 /// ```
102 /// use macro_tools ::exposed :: *;
103 ///
104 /// let original_input: proc_macro2 ::TokenStream = quote!
105 /// {
106 /// #[ derive( Debug, PartialEq ) ]
107 /// pub struct MyStruct
108 /// {
109 /// pub field: i32,
110 /// }
111 /// };
112 ///
113 /// let generated_code: proc_macro2 ::TokenStream = quote!
114 /// {
115 /// impl MyStruct
116 /// {
117 /// pub fn new( field: i32 ) -> Self
118 /// {
119 /// MyStruct { field }
120 /// }
121 /// }
122 /// };
123 ///
124 /// // Format the debug report for printing or logging
125 /// let formatted_report = report_format( &"Code Transformation for MyStruct", &original_input, generated_code );
126 /// println!( "{}", formatted_report );
127 /// ```
128 ///
129 #[ allow( clippy ::needless_pass_by_value ) ]
130 pub fn report_format< IntoAbout, IntoInput, IntoOutput >(about: IntoAbout, input: IntoInput, output: IntoOutput) -> String
131 where
132 IntoAbout: ToString,
133 IntoInput: ToString,
134 IntoOutput: ToString,
135 {
136 "\n".to_string()
137 + &format!(" = context\n\n{}\n\n", indentation(" ", about.to_string(), ""))
138 + &format!(" = original\n\n{}\n\n", indentation(" ", input.to_string(), ""))
139 + &format!(" = generated\n\n{}\n", indentation(" ", output.to_string(), ""))
140 }
141
142 /// Prints a debugging report for a pair of token streams to the standard output.
143 ///
144 /// This function acts as a utility for debugging transformations in procedural macros or other code generation scenarios.
145 /// It provides an immediate visual comparison of the original code versus the generated code by utilizing the `report_format`
146 /// function to format the output and then printing it directly to the standard output. This can be particularly helpful for
147 /// real-time debugging and quick assessments without requiring additional output management.
148 ///
149 /// # Parameters and Type Parameters
150 /// - `about` : A description of the code transformation context or operation. This is used to headline the generated report.
151 /// - `input` : The original code or token stream before transformation. This is what the code looked like prior to any procedural manipulations.
152 /// - `output` : The transformed or generated code or token stream as a result of the macro or code transformation process.
153 ///
154 /// The types for these parameters are expected to be convertible to strings, matching the `report_format` function's requirements.
155 ///
156 /// # Examples
157 ///
158 /// ```rust
159 /// use macro_tools ::exposed :: *;
160 ///
161 /// let original_input: proc_macro2 ::TokenStream = quote!
162 /// {
163 /// #[ derive( Debug, PartialEq ) ]
164 /// pub struct MyStruct
165 /// {
166 /// pub field: i32,
167 /// }
168 /// };
169 ///
170 /// let generated_code: proc_macro2 ::TokenStream = quote!
171 /// {
172 /// impl MyStruct
173 /// {
174 /// pub fn new( field: i32 ) -> Self
175 /// {
176 /// MyStruct { field }
177 /// }
178 /// }
179 /// };
180 ///
181 /// // Directly print the debug report
182 /// report_print( "Code Transformation for MyStruct", original_input, generated_code );
183 /// ```
184 ///
185 /// The above example demonstrates how the `report_print` function can be used to visualize the changes from original input code to the generated code,
186 /// helping developers to verify and understand the modifications made during code generation processes. The output is formatted to show clear distinctions
187 /// between the 'original' and 'generated' sections, providing an easy-to-follow comparison.
188 pub fn report_print< IntoAbout, IntoInput, IntoOutput >(about: IntoAbout, input: IntoInput, output: IntoOutput)
189 where
190 IntoAbout: ToString,
191 IntoInput: ToString,
192 IntoOutput: ToString,
193 {
194 println!("{}", report_format(about, input, output));
195 }
196
197 ///
198 /// Macro for diagnostics purpose to print both syntax tree and source code behind it with syntax tree.
199 ///
200 /// ### Basic use-case.
201 /// ```
202 /// use macro_tools ::prelude :: *;
203 ///
204 /// let code = qt!( std ::collections ::HashMap< i32, i32 > );
205 /// let tree_type = syn ::parse2 :: < syn ::Type >( code ).unwrap();
206 /// tree_print!( tree_type );
207 /// ```
208 ///
209 #[ macro_export ]
210 macro_rules! tree_print
211 {
212 ( $src: expr ) =>
213 {{
214 let result = $crate ::tree_diagnostics_str!( $src );
215 println!( "{}", result );
216 result
217 }};
218 ( $( $src: expr ),+ $(,)? ) =>
219 {{
220 $( $crate ::tree_print!( $src ) );+
221 }};
222 }
223
224 ///
225 /// Macro for diagnostics purpose to print both syntax tree and source code behind it without syntax tree.
226 ///
227 /// ### Basic use-case.
228 /// ```
229 /// use macro_tools ::prelude :: *;
230 ///
231 /// let code = qt!( std ::collections ::HashMap< i32, i32 > );
232 /// let tree_type = syn ::parse2 :: < syn ::Type >( code ).unwrap();
233 /// tree_print!( tree_type );
234 /// ```
235 ///
236 #[ macro_export ]
237 macro_rules! code_print
238 {
239 ( $src: expr ) =>
240 {{
241 let result = $crate ::code_diagnostics_str!( $src );
242 println!( "{}", result );
243 result
244 }};
245 ( $( $src: expr ),+ $(,)? ) =>
246 {{
247 $( $crate ::code_print!( $src ) );+
248 }};
249 }
250
251 /// Custom debug formatter that uses 2-space indentation per project style requirements.
252 ///
253 /// This function formats debug output using 2-space indentation instead of Rust's
254 /// default 4-space indentation, ensuring compliance with the project's codestyle rulebook.
255 ///
256 /// # Parameters
257 /// * `value` - Any type implementing Debug trait to be formatted
258 ///
259 /// # Returns
260 /// A String containing the debug representation with 2-space indentation
261 ///
262 /// # Examples
263 /// ```
264 /// use macro_tools ::diag ::debug_2_space;
265 /// use std ::collections ::HashMap;
266 ///
267 /// let mut map = HashMap ::new();
268 /// map.insert("key", "value");
269 /// let formatted = debug_2_space(&map);
270 /// // Output uses 2-space indentation instead of default 4-space
271 /// ```
272 ///
273 /// **Rationale** : Satisfies the codestyle rulebook requirement for 2-space indentation
274 /// universally applied to all code, including debug output formatting.
275 pub fn debug_2_space< T: core ::fmt ::Debug >(value: &T) -> String
276 {
277 let standard_debug = format!("{value:#?}");
278 // Convert all 4-space indentation to 2-space per codestyle rules
279 // Process line by line to handle nested indentation correctly
280 standard_debug
281 .lines()
282 .map(|line| {
283 let leading_spaces = line.len() - line.trim_start().len();
284 let indent_level = leading_spaces / 4;
285 let remainder_spaces = leading_spaces % 4;
286 let new_indent = " ".repeat(indent_level) + &" ".repeat(remainder_spaces);
287 format!("{}{}", new_indent, line.trim_start())
288 })
289 .collect :: < Vec<_ >>()
290 .join("\n")
291 }
292
293 ///
294 /// Macro for diagnostics purpose to export both syntax tree and source code behind it into a string.
295 ///
296 #[ macro_export ]
297 macro_rules! tree_diagnostics_str {
298 ( $src: expr ) => {{
299 let src2 = &$src;
300 format!("{} : {} :\n{}", stringify!($src), $crate ::qt! { #src2 }, $crate ::diag ::debug_2_space(&$src))
301 }};
302 }
303
304 ///
305 /// Macro for diagnostics purpose to diagnose source code behind it and export it into a string.
306 ///
307 #[ macro_export ]
308 macro_rules! code_diagnostics_str {
309 ( $src: expr ) => {{
310 let src2 = &$src;
311 format!("{} : {}", stringify!($src), $crate ::qt! { #src2 })
312 }};
313 }
314
315 ///
316 /// Macro to export source code behind a syntax tree into a string.
317 ///
318 #[ macro_export ]
319 macro_rules! code_to_str {
320 ( $src: expr ) => {{
321 let src2 = &$src;
322 format!("{}", $crate ::qt! { #src2 })
323 }};
324 }
325
326 ///
327 /// Macro to generate syn error either with span of a syntax tree element or with default one `proc_macro2 ::Span ::call_site()`.
328 ///
329 /// ### Basic use-case.
330 /// ```
331 /// # use macro_tools ::exposed :: *;
332 /// syn_err!( "No attr" );
333 /// # ()
334 /// ```
335 ///
336 #[ macro_export ]
337 macro_rules! syn_err
338 {
339
340 ( $msg: expr $(,)? ) =>
341 {
342 $crate ::syn ::Error ::new( proc_macro2 ::Span ::call_site(), $msg )
343 };
344 ( _, $msg: expr $(,)? ) =>
345 {
346 $crate ::syn ::Error ::new( proc_macro2 ::Span ::call_site(), $msg )
347 };
348 ( $span: expr, $msg: expr $(,)? ) =>
349 {
350 $crate ::syn ::Error ::new( syn ::spanned ::Spanned ::span( &( $span ) ), $msg )
351 };
352 ( $span: expr, $msg: expr, $( $arg: expr ),+ $(,)? ) =>
353 {
354 $crate ::syn ::Error ::new( syn ::spanned ::Spanned ::span( &( $span ) ), format!( $msg, $( $arg ),+ ) )
355 };
356 ( _, $msg: expr, $( $arg: expr ),+ $(,)? ) =>
357 {
358 $crate ::syn ::Error ::new( proc_macro2 ::Span ::call_site(), format!( $msg, $( $arg ),+ ) )
359 };
360
361 }
362
363 ///
364 /// Macro to generate syn error either with span of a syntax tree element or with default one `proc_macro2 ::Span ::call_site()`.
365 ///
366 /// ### Basic use-case.
367 /// ```
368 /// # use macro_tools ::exposed :: *;
369 /// syn_err!( "No attr" );
370 /// # ()
371 /// ```
372 ///
373 #[ macro_export ]
374 macro_rules! return_syn_err
375 {
376 ( $( $Arg: tt )* ) =>
377 {
378 return Result ::Err( $crate ::syn_err!( $( $Arg )* ) )
379 };
380 }
381
382 pub use { tree_print, code_print, tree_diagnostics_str, code_diagnostics_str, code_to_str, syn_err, return_syn_err };
383}
384
385#[ doc( inline ) ]
386#[ allow( unused_imports ) ]
387pub use own :: *;
388
389/// Own namespace of the module.
390#[ allow( unused_imports ) ]
391pub mod own
392{
393
394 use super :: *;
395
396 #[ doc( inline ) ]
397 pub use orphan :: *;
398}
399
400/// Parented namespace of the module.
401#[ allow( unused_imports ) ]
402pub mod orphan
403{
404
405 use super :: *;
406 #[ doc( inline ) ]
407 pub use exposed :: *;
408
409 // #[ doc( inline ) ]
410 // #[ allow( unused_imports ) ]
411 // pub use private ::
412 // {
413 // Result,
414 // };
415}
416
417/// Exposed namespace of the module.
418#[ allow( unused_imports ) ]
419pub mod exposed
420{
421
422 use super :: *;
423 pub use super ::super ::diag;
424
425 #[ doc( inline ) ]
426 pub use prelude :: *;
427
428 #[ doc( inline ) ]
429 pub use private :: { indentation, report_format, report_print, debug_2_space };
430}
431
432/// Prelude to use essentials: `use my_module ::prelude :: *`.
433#[ allow( unused_imports ) ]
434pub mod prelude
435{
436
437 use super :: *;
438
439 #[ doc( inline ) ]
440 pub use private :: { tree_print, code_print, tree_diagnostics_str, code_diagnostics_str, code_to_str, syn_err, return_syn_err };
441
442 // #[ doc( inline ) ]
443 // pub use private ::Result;
444}