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}