iprint/
lib.rs

1//! # iprint-rs
2//!
3//! `iprint-rs` is a Rust utility library for indented printing and logging,
4//! designed to help you easily trace the execution of your code. The library
5//! provides a suite of macros for indented formatting (`iformat`), simplified
6//! indented printing (`iprintln`), and logging with varying levels
7//! (`itrace`, `idebug`, `iinfo`, `iwarn`, `ierror`). All features are geared
8//! to improve debugging and code tracing by automatically adjusting indentation
9//! based on the function call depth.
10//!
11//! ## ⚠️ Warning
12//!
13//! This library may not function correctly when compiled in release mode due
14//! to function inlining. It is recommended to use it in debug mode for accurate results.
15//!
16//! ## Features
17//!
18//! - **iprintln! macro**: This is an enhanced version of `println!`, adding automatic indentation.
19//! - **iformat! macro**: Allows for custom indented formatting.
20//! - **call_depth! macro**: Provides the current depth of the function call stack,
21//!   useful for custom logging or tracing solutions.
22//! - **indented logging**: Offers five levels of logging (`itrace`, `idebug`, `iinfo`,
23//!   `iwarn`, `ierror`) that are feature-gated by the `log` feature.
24//!
25//! ## Installation
26//!
27//! To use the library, include it in your `Cargo.toml` like so:
28//!
29//! ```toml
30//! [dependencies]
31//! iprint = "0.1.4"  # Use the latest version
32//! ```
33//!
34//! To enable the `log` feature for additional logging functionalities:
35//!
36//! ```toml
37//! [dependencies]
38//! iprint = { version = "0.1.4", features = ["log"] }
39//! ```
40//!
41//! ## Usage Examples
42//!
43//! ### iprintln! macro
44//!
45//! ```rust
46//! use iprint::iprintln;
47//!
48//! fn my_function() {
49//!     iprintln!("This is like println! but with automatic indentation.");
50//!     another_function();
51//!     iprintln!("This will be indented like the first iprintln!");
52//! }
53//!
54//! fn another_function() {
55//!     iprintln!("This message will be more indented.");
56//! }
57//! ```
58//!
59//! ### iformat! macro
60//!
61//! ```rust
62//! use iprint::iformat;
63//!
64//! fn my_function() {
65//!     let msg = iformat!("This will be indented based on call depth.");
66//!     println!("{}", msg);
67//! }
68//! ```
69//!
70//! ### call_depth! macro
71//!
72//! ```rust
73//! use iprint::call_depth;
74//!
75//! fn custom_logging() {
76//!     let depth = call_depth!();
77//!     println!("Current call depth: {}", depth);
78//! }
79//! ```
80//!
81//! ### Logging Functions (Feature-Gated)
82//!
83//! To use logging functions, make sure you have the `log` feature enabled.
84//!
85//! ```rust
86//! #[cfg(feature = "log")]
87//! {
88//!     use iprint::iinfo;
89//!
90//!     fn yet_another_function() {
91//!         iinfo!("This is an informational message with automatic indentation.");
92//!     }
93//! }
94//! ```
95
96use std::cell::RefCell;
97
98thread_local!(
99    #[doc(hidden)]
100    pub static STACK: RefCell<Vec<usize>> = RefCell::new(vec![])
101);
102
103#[doc(hidden)]
104#[macro_export]
105macro_rules! stack_ptr {
106    () => ({
107        let mut rsp: usize;
108        unsafe {
109            core::arch::asm!("mov {}, rsp", out(reg) rsp);
110        }
111        rsp
112    })
113}
114
115/// Retrieves the current call depth of the function stack.
116///
117/// This macro returns an integer representing the depth of the function
118/// call stack at the point where it is invoked.
119///
120/// # Example
121///
122/// ```
123/// use iprint::call_depth;
124///
125/// fn main() {
126///     // Call depth here should be 0, since this is the main function.
127///     assert_eq!(call_depth!(), 0);
128///
129///     // Call first_function(), this will increase the call depth.
130///     first_function();
131///
132///     // Call depth here should be 0 again
133///     assert_eq!(call_depth!(), 0);
134/// }
135///
136/// fn first_function() {
137///     // Call depth here should be 1, since we're one function deep from main.
138///     assert_eq!(call_depth!(), 1);
139///
140///     // Call second_function(), this will increase the call depth.
141///     second_function();
142///
143///     // Call depth here should be 1 again
144///     assert_eq!(call_depth!(), 1);
145/// }
146///
147/// fn second_function() {
148///     // Call depth here should be 2, since we're two functions deep from main.
149///     assert_eq!(call_depth!(), 2);
150/// }
151/// ```
152#[macro_export]
153macro_rules! call_depth {
154    () => {{
155        let stack_pointer = $crate::stack_ptr!();
156        $crate::STACK.with(|c| {
157            let mut stack = c.borrow_mut();
158            while let Some(&last) = stack.last() {
159                if last < stack_pointer {
160                    stack.pop();
161                } else {
162                    break;
163                }
164            }
165            if stack.last() != Some(&stack_pointer) {
166                stack.push(stack_pointer);
167            }
168            stack.len() - 1
169        })
170    }};
171}
172
173/// Formats a given string with indentation based on the current call depth.
174///
175/// This macro works similarly to Rust's built-in `format!` macro,
176/// but prepends an indentation to the formatted string. The level of
177/// indentation is determined by the current call depth in the stack.
178///
179/// # Example
180///
181/// ```
182/// use iprint::iformat;
183///
184/// fn my_function() {
185///     let msg = iformat!("This will be indented based on call depth.");
186///     println!("{}", msg);
187/// }
188/// ```
189#[macro_export]
190macro_rules! iformat {
191    ($($t:tt)*) => {{
192        let call_depth = $crate::call_depth!();
193        let indent = 4 * call_depth;
194        let text = format!($($t)*);
195        let indented_text: String = text
196            .lines()
197            .map(|line| format!("{:indent$}{}", "", line, indent=indent))
198            .collect::<Vec<_>>()
199            .join("\n");
200        indented_text
201    }}
202}
203
204/// Prints a given string with automatic indentation to the console.
205///
206/// This macro is an enhanced version of Rust's `println!` macro,
207/// adding automatic indentation based on the current call depth.
208///
209/// # Example
210///
211/// ```
212/// use iprint::iprintln;
213///
214/// fn another_function() {
215///     iprintln!("This is like println! but with automatic indentation.");
216/// }
217/// ```
218#[macro_export]
219macro_rules! iprintln {
220    ($($t:tt)*) => {
221        println!("{}", $crate::iformat!($($t)*))
222    }
223}
224
225#[cfg(feature = "log")]
226pub mod ilog {
227    /// Logs a trace message with automatic indentation.
228    ///
229    /// This macro is an enhanced version of the `trace!` macro from the `log` crate,
230    /// adding automatic indentation based on the current call depth.
231    ///
232    /// # Example
233    ///
234    /// ```
235    /// #[cfg(feature = "log")]
236    /// {
237    ///     use iprint::itrace;
238    ///
239    ///     fn my_trace_function() {
240    ///         itrace!("This is an informational message with automatic indentation.");
241    ///     }
242    /// }
243    /// ```
244    /// This macro is available only if the "log" feature is enabled.
245    #[macro_export]
246    macro_rules! itrace {
247        ($($t:tt)*) => {
248            trace!("{}", $crate::iformat!($($t)*))
249        }
250    }
251
252    /// Logs a debug message with automatic indentation.
253    ///
254    /// This macro is an enhanced version of the `debug!` macro from the `log` crate,
255    /// adding automatic indentation based on the current call depth.
256    ///
257    /// # Example
258    ///
259    /// ```rust
260    /// #[cfg(feature = "log")]
261    /// {
262    ///     use iprint::idebug;
263    ///
264    ///     fn my_debug_function() {
265    ///         idebug!("This is a debug message with automatic indentation.");
266    ///     }
267    /// }
268    /// ```
269    ///
270    /// This macro is available only if the "log" feature is enabled.
271    #[macro_export]
272    macro_rules! idebug {
273        ($($t:tt)*) => {
274            debug!("{}", $crate::iformat!($($t)*))
275        }
276    }
277
278    /// Logs an informational message with automatic indentation.
279    ///
280    /// This macro is an enhanced version of the `info!` macro from the `log` crate,
281    /// adding automatic indentation based on the current call depth.
282    ///
283    /// # Example
284    ///
285    /// ```rust
286    /// #[cfg(feature = "log")]
287    /// {
288    ///     use iprint::iinfo;
289    ///
290    ///     fn my_info_function() {
291    ///     iinfo!("This is an informational message with automatic indentation.");
292    /// }
293    /// }
294    /// ```
295    ///
296    /// This macro is available only if the "log" feature is enabled.
297    #[macro_export]
298    macro_rules! iinfo {
299        ($($t:tt)*) => {
300            info!("{}", $crate::iformat!($($t)*))
301        }
302    }
303
304    /// Logs a warning message with automatic indentation.
305    ///
306    /// This macro is an enhanced version of the `warn!` macro from the `log` crate,
307    /// adding automatic indentation based on the current call depth.
308    ///
309    /// # Example
310    ///
311    /// ```rust
312    ///#[cfg(feature = "log")]
313    /// {
314    ///     use iprint::iwarn;
315    ///
316    ///     fn my_warn_function() {
317    ///         iwarn!("This is a warning message with automatic indentation.");
318    ///     }
319    /// }
320    /// ```
321    ///
322    /// This macro is available only if the "log" feature is enabled.
323    #[macro_export]
324    macro_rules! iwarn {
325        ($($t:tt)*) => {
326            warn!("{}", $crate::iformat!($($t)*))
327        }
328    }
329
330    /// Logs an error message with automatic indentation.
331    ///
332    /// This macro is an enhanced version of the `error!` macro from the `log` crate,
333    /// adding automatic indentation based on the current call depth.
334    ///
335    /// # Example
336    ///
337    /// ```rust
338    /// #[cfg(feature = "log")]
339    /// {
340    ///     use iprint::ierror;
341    ///
342    ///     fn my_error_function() {
343    ///         ierror!("This is an error message with automatic indentation.");
344    ///     }
345    /// }
346    /// ```
347    ///
348    /// This macro is available only if the "log" feature is enabled.
349    #[macro_export]
350    macro_rules! ierror {
351        ($($t:tt)*) => {
352            error!("{}", $crate::iformat!($($t)*))
353        }
354    }
355}