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}