macro_tools/
diag.rs

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