Skip to main content

kasl/libs/messages/
macros.rs

1//! Convenient macros for application messaging and logging.
2//!
3//! Provides a comprehensive set of macros that simplify message display and logging throughout the application with automatic debug mode detection.
4//!
5//! ## Features
6//!
7//! - **Dual Output Mode**: Automatic switching between tracing and console output
8//! - **Debug Detection**: Runtime detection of debug mode configuration
9//! - **Message Categorization**: Different macros for different message types
10//! - **Performance Optimization**: Cached debug mode detection for efficiency
11//! - **Error Handling**: Specialized macros for error creation and handling
12//!
13//! ## Usage
14//!
15//! ```rust
16//! use kasl::{msg_info, msg_error, msg_success, msg_warning};
17//! use kasl::libs::messages::types::Message;
18//!
19//! // Basic message display
20//! msg_info!(Message::TaskCreated);
21//! msg_success!(Message::DailyReportSent("2025-01-15".to_string()));
22//! msg_error!(Message::ConfigSaveError);
23//!
24//! // Custom formatted messages
25//! let count = 5;
26//! msg_info!(format!("Processing {} items", count));
27//! ```
28
29/// Convenience macros for common message operations with conditional tracing support
30use std::sync::OnceLock;
31
32/// Global cache for debug mode detection to avoid repeated environment variable checks.
33///
34/// This static variable uses `OnceLock` to cache the result of debug mode detection
35/// on first access. This provides significant performance benefits by avoiding
36/// repeated environment variable lookups, which can be expensive operations.
37///
38/// ## Performance Benefits
39/// - **Single Check**: Environment variables are checked only once per application run
40/// - **Fast Access**: Subsequent checks are simple memory reads
41/// - **Thread Safety**: OnceLock provides thread-safe initialization
42/// - **Memory Efficiency**: Minimal memory overhead for caching
43static DEBUG_MODE: OnceLock<bool> = OnceLock::new();
44
45/// Checks if debug mode is enabled, with caching for performance.
46///
47/// This function determines whether the application is running in debug mode
48/// by checking for the presence of debug-related environment variables. The
49/// result is cached using `OnceLock` to avoid repeated expensive environment
50/// variable lookups.
51///
52/// ## Detection Logic
53///
54/// Debug mode is considered enabled if either of these environment variables is set:
55/// - **`KASL_DEBUG`**: Application-specific debug flag
56/// - **`RUST_LOG`**: Standard Rust logging configuration
57///
58/// The presence of either variable indicates that the user wants enhanced
59/// logging output and expects debug information to be available.
60///
61/// ## Caching Strategy
62///
63/// The function uses a lazy initialization pattern:
64/// 1. **First Call**: Checks environment variables and caches result
65/// 2. **Subsequent Calls**: Returns cached value without environment checks
66/// 3. **Thread Safety**: Multiple threads can safely call this function
67/// 4. **Performance**: Subsequent calls are essentially free
68///
69/// ## Integration Points
70///
71/// This function is used by all message macros to determine output routing:
72/// - **Debug Mode**: Messages go to tracing system with structured logging
73/// - **Normal Mode**: Messages go to simple console output (println!/eprintln!)
74///
75/// # Returns
76///
77/// Returns `true` if debug mode is enabled, `false` otherwise. The result
78/// is cached for the lifetime of the application.
79///
80/// # Examples
81///
82/// ```rust
83/// use kasl::libs::messages::macros::is_debug_mode;
84///
85/// if is_debug_mode() {
86///     println!("Running in debug mode with enhanced logging");
87/// } else {
88///     println!("Running in normal mode with simple output");
89/// }
90/// ```
91#[doc(hidden)]
92pub fn is_debug_mode() -> bool {
93    *DEBUG_MODE.get_or_init(|| {
94        // Check for application-specific debug flag
95        std::env::var("KASL_DEBUG").is_ok() ||
96        // Check for standard Rust logging configuration
97        std::env::var("RUST_LOG").is_ok()
98    })
99}
100
101/// Prints a general message with automatic debug mode routing.
102///
103/// This macro provides the basic message display functionality with automatic
104/// detection of debug mode to route output appropriately. It supports both
105/// simple single-line messages and formatted messages with optional line breaks.
106///
107/// ## Output Routing
108///
109/// - **Debug Mode**: Uses `tracing::info!` for structured logging
110/// - **Normal Mode**: Uses `println!` for simple console output
111///
112/// ## Usage Patterns
113///
114/// ### Simple Message
115/// ```rust
116/// use kasl::msg_print;
117/// use kasl::libs::messages::types::Message;
118///
119/// msg_print!(Message::ConfigSaved);
120/// // Output: "Configuration saved successfully"
121/// ```
122///
123/// ### Message with Line Breaks
124/// ```rust
125/// use kasl::msg_print;
126/// use kasl::libs::messages::types::Message;
127///
128/// msg_print!(Message::ReportHeader("2025-01-15".to_string()), true);
129/// // Output: "\nšŸ“Š Daily Work Report\n"
130/// ```
131///
132/// ## Performance Notes
133///
134/// - Debug mode detection is cached for efficiency
135/// - Tracing integration provides structured logging in debug mode
136/// - Simple println! provides fast output in production mode
137#[macro_export]
138macro_rules! msg_print {
139    ($msg:expr) => {
140        if $crate::libs::messages::macros::is_debug_mode() {
141            tracing::info!("{}", $msg);
142        } else {
143            println!("{}", $msg);
144        }
145    };
146    ($msg:expr, true) => {
147        if $crate::libs::messages::macros::is_debug_mode() {
148            tracing::info!("\n{}\n", $msg);
149        } else {
150            println!("\n{}\n", $msg);
151        }
152    };
153}
154
155/// Prints a success message with āœ… prefix and automatic routing.
156///
157/// This macro is specifically designed for displaying success notifications
158/// and positive confirmations. The green checkmark emoji provides visual
159/// confirmation that operations completed successfully.
160///
161/// ## Visual Design
162///
163/// - **Prefix**: āœ… (green checkmark emoji)
164/// - **Purpose**: Success confirmations and positive outcomes
165/// - **Examples**: Task creation, configuration saves, successful exports
166///
167/// ## Output Examples
168///
169/// ```text
170/// āœ… Task created successfully
171/// āœ… Configuration saved successfully
172/// āœ… Data exported to file.csv
173/// ```
174///
175/// ## Usage Patterns
176///
177/// ### Simple Success Message
178/// ```rust
179/// use kasl::msg_success;
180/// use kasl::libs::messages::types::Message;
181///
182/// msg_success!(Message::TaskCreated);
183/// // Output: "āœ… Task created successfully"
184/// ```
185///
186/// ### Success Message with Line Breaks
187/// ```rust
188/// use kasl::msg_success;
189/// use kasl::libs::messages::types::Message;
190///
191/// msg_success!(Message::ExportCompleted("data.csv".to_string()), true);
192/// // Output: "\nāœ… Data exported successfully to: data.csv\n"
193/// ```
194#[macro_export]
195macro_rules! msg_success {
196    ($msg:expr) => {
197        if $crate::libs::messages::macros::is_debug_mode() {
198            tracing::info!("āœ… {}", $msg);
199        } else {
200            println!("āœ… {}", $msg);
201        }
202    };
203    ($msg:expr, true) => {
204        if $crate::libs::messages::macros::is_debug_mode() {
205            tracing::info!("\nāœ… {}\n", $msg);
206        } else {
207            println!("\nāœ… {}\n", $msg);
208        }
209    };
210}
211
212/// Prints an error message with āŒ prefix and automatic routing.
213///
214/// This macro handles error message display with appropriate severity level
215/// routing. In debug mode, errors are logged through the tracing system,
216/// while in normal mode they're displayed on stderr for proper error handling.
217///
218/// ## Visual Design
219///
220/// - **Prefix**: āŒ (red X emoji)
221/// - **Purpose**: Error notifications and failure messages
222/// - **Stream**: Uses stderr in normal mode for proper error stream handling
223///
224/// ## Output Routing
225///
226/// - **Debug Mode**: Uses `tracing::error!` for structured error logging
227/// - **Normal Mode**: Uses `eprintln!` to write to stderr
228///
229/// ## Error Stream Benefits
230///
231/// Using stderr for error output provides several advantages:
232/// - **Stream Separation**: Errors don't interfere with normal output
233/// - **Script Compatibility**: Scripts can separate errors from data
234/// - **Shell Redirection**: Users can redirect errors independently
235/// - **Log Aggregation**: Error logs can be collected separately
236///
237/// ## Usage Patterns
238///
239/// ### Simple Error Message
240/// ```rust
241/// use kasl::msg_error;
242/// use kasl::libs::messages::types::Message;
243///
244/// msg_error!(Message::TaskNotFound);
245/// // Output to stderr: "āŒ Task not found"
246/// ```
247///
248/// ### Error Message with Line Breaks
249/// ```rust
250/// use kasl::msg_error;
251/// use kasl::libs::messages::types::Message;
252///
253/// msg_error!(Message::ConfigParseError, true);
254/// // Output to stderr: "\nāŒ Failed to parse configuration file\n"
255/// ```
256#[macro_export]
257macro_rules! msg_error {
258    ($msg:expr) => {
259        if $crate::libs::messages::macros::is_debug_mode() {
260            tracing::error!("āŒ {}", $msg);
261        } else {
262            eprintln!("āŒ {}", $msg);
263        }
264    };
265    ($msg:expr, true) => {
266        if $crate::libs::messages::macros::is_debug_mode() {
267            tracing::error!("\nāŒ {}\n", $msg);
268        } else {
269            eprintln!("\nāŒ {}\n", $msg);
270        }
271    };
272}
273
274/// Prints a warning message with āš ļø prefix and automatic routing.
275///
276/// This macro displays warning messages that indicate potential issues or
277/// situations requiring user attention, but which don't prevent operation
278/// from continuing. Warnings help users understand system state and make
279/// informed decisions.
280///
281/// ## Visual Design
282///
283/// - **Prefix**: āš ļø (warning triangle emoji)
284/// - **Purpose**: Cautionary messages and non-critical issues
285/// - **Severity**: Less critical than errors, more important than info
286///
287/// ## Warning Categories
288///
289/// Warnings are appropriate for:
290/// - **Deprecated Features**: Features that will be removed in future versions
291/// - **Configuration Issues**: Non-critical configuration problems
292/// - **Performance Concerns**: Operations that may be slow or inefficient
293/// - **Fallback Behavior**: When the system falls back to default behavior
294/// - **Resource Limitations**: When approaching resource limits
295///
296/// ## Usage Patterns
297///
298/// ### Simple Warning Message
299/// ```rust
300/// use kasl::msg_warning;
301/// use kasl::libs::messages::types::Message;
302///
303/// msg_warning!(Message::AutostartCheckingAlternative);
304/// // Output: "āš ļø Checking alternative autostart methods..."
305/// ```
306///
307/// ### Warning Message with Line Breaks
308/// ```rust
309/// use kasl::msg_warning;
310/// use kasl::libs::messages::types::Message;
311///
312/// msg_warning!(Message::WatcherSignalHandlingNotSupported, true);
313/// // Output: "\nāš ļø Signal handling not supported on this platform\n"
314/// ```
315#[macro_export]
316macro_rules! msg_warning {
317    ($msg:expr) => {
318        if $crate::libs::messages::macros::is_debug_mode() {
319            tracing::warn!("āš ļø {}", $msg);
320        } else {
321            println!("āš ļø {}", $msg);
322        }
323    };
324    ($msg:expr, true) => {
325        if $crate::libs::messages::macros::is_debug_mode() {
326            tracing::warn!("\nāš ļø {}\n", $msg);
327        } else {
328            println!("\nāš ļø {}\n", $msg);
329        }
330    };
331}
332
333/// Prints an informational message with ā„¹ļø prefix and automatic routing.
334///
335/// This macro displays informational messages that provide useful context
336/// or status updates to users. Info messages help users understand what
337/// the system is doing and provide transparency into system operations.
338///
339/// ## Visual Design
340///
341/// - **Prefix**: ā„¹ļø (information emoji)
342/// - **Purpose**: Status updates and informational content
343/// - **Tone**: Neutral, informative, helpful
344///
345/// ## Information Categories
346///
347/// Info messages are appropriate for:
348/// - **Status Updates**: Progress information for long-running operations
349/// - **System State**: Current system status and configuration
350/// - **Process Information**: What the system is currently doing
351/// - **User Guidance**: Helpful tips and usage information
352/// - **Confirmation**: Non-critical confirmations and acknowledgments
353///
354/// ## Usage Patterns
355///
356/// ### Simple Info Message
357/// ```rust
358/// use kasl::msg_info;
359/// use kasl::libs::messages::types::Message;
360///
361/// msg_info!(Message::WatcherStarted(1234));
362/// // Output: "ā„¹ļø Watcher started with PID: 1234"
363/// ```
364///
365/// ### Info Message with Line Breaks
366/// ```rust
367/// use kasl::msg_info;
368/// use kasl::libs::messages::types::Message;
369///
370/// msg_info!(Message::WorkingHoursForMonth("2025-01".to_string()), true);
371/// // Output: "\nā„¹ļø šŸ“… Monthly Summary\n"
372/// ```
373#[macro_export]
374macro_rules! msg_info {
375    ($msg:expr) => {
376        if $crate::libs::messages::macros::is_debug_mode() {
377            tracing::info!("ā„¹ļø {}", $msg);
378        } else {
379            println!("ā„¹ļø {}", $msg);
380        }
381    };
382    ($msg:expr, true) => {
383        if $crate::libs::messages::macros::is_debug_mode() {
384            tracing::info!("\nā„¹ļø {}\n", $msg);
385        } else {
386            println!("\nā„¹ļø {}\n", $msg);
387        }
388    };
389}
390
391/// Debug-only message display with šŸ” prefix.
392///
393/// This macro provides debug-specific logging that only appears when debug
394/// mode is explicitly enabled. Debug messages are useful for troubleshooting
395/// and development but are hidden from normal users to avoid clutter.
396///
397/// ## Debug-Only Behavior
398///
399/// - **Debug Mode**: Messages are displayed using `tracing::debug!`
400/// - **Normal Mode**: Messages are completely suppressed (no output)
401/// - **Performance**: No overhead in production builds when debug is disabled
402///
403/// ## Visual Design
404///
405/// - **Prefix**: šŸ” (magnifying glass emoji)
406/// - **Purpose**: Development and troubleshooting information
407/// - **Audience**: Developers and power users debugging issues
408///
409/// ## Debug Message Categories
410///
411/// Debug messages are appropriate for:
412/// - **Technical Details**: Low-level system information
413/// - **State Changes**: Internal state transitions and updates
414/// - **Performance Metrics**: Timing and performance measurements
415/// - **Data Flow**: How data moves through the system
416/// - **Error Context**: Additional context for debugging errors
417///
418/// ## Usage Patterns
419///
420/// ### Technical Debug Information
421/// ```rust
422/// use kasl::msg_debug;
423///
424/// let task_id = 42;
425/// msg_debug!(format!("Processing task with ID: {}", task_id));
426/// // Debug mode output: "šŸ” Processing task with ID: 42"
427/// // Normal mode output: (nothing)
428/// ```
429///
430/// ### State Change Debugging
431/// ```rust
432/// use kasl::msg_debug;
433///
434/// let old_state = "Active";
435/// let new_state = "InPause";
436/// msg_debug!(format!("State transition: {:?} -> {:?}", old_state, new_state));
437/// // Debug mode output: "šŸ” State transition: Active -> InPause"
438/// // Normal mode output: (nothing)
439/// ```
440#[macro_export]
441macro_rules! msg_debug {
442    ($msg:expr) => {
443        if $crate::libs::messages::macros::is_debug_mode() {
444            tracing::debug!("šŸ” {}", $msg);
445        }
446    };
447}
448
449/// Creates an `anyhow::Error` from a message with āŒ prefix.
450///
451/// This macro provides a convenient way to create `anyhow::Error` instances
452/// from application messages. It's useful for error propagation in functions
453/// that return `Result<T, anyhow::Error>` and need to convert application
454/// messages into proper error types.
455///
456/// ## Error Creation Strategy
457///
458/// - **Prefix Addition**: Automatically adds āŒ prefix for visual consistency
459/// - **Error Propagation**: Creates errors suitable for `?` operator use
460/// - **Message Integration**: Works with the application's message system
461/// - **Type Compatibility**: Returns `anyhow::Error` for easy integration
462///
463/// ## Use Cases
464///
465/// ### Function Error Returns
466/// ```rust
467/// use anyhow::Result;
468/// use kasl::{msg_error_anyhow, libs::messages::Message};
469///
470/// # fn config_is_invalid() -> bool { false }
471/// fn validate_config() -> Result<()> {
472///     if config_is_invalid() {
473///         return Err(msg_error_anyhow!(Message::ConfigParseError));
474///     }
475///     Ok(())
476/// }
477/// ```
478///
479/// ### Error Context Addition
480/// ```rust
481/// use anyhow::{Result, Context};
482/// use kasl::{msg_error_anyhow, libs::messages::Message};
483///
484/// # fn some_operation() -> Result<()> { Ok(()) }
485/// fn complex_operation() -> Result<()> {
486///     some_operation()
487///         .context(msg_error_anyhow!(Message::TaskUpdateFailed))
488/// }
489/// ```
490///
491/// ## Error Handling Benefits
492///
493/// - **Consistent Formatting**: All errors have consistent visual presentation
494/// - **Message Reuse**: Leverages existing message definitions
495/// - **Type Safety**: Provides proper error types for Rust's error handling
496/// - **Integration**: Works seamlessly with `anyhow` and `?` operator
497#[macro_export]
498macro_rules! msg_error_anyhow {
499    ($msg:expr) => {
500        anyhow::anyhow!("āŒ {}", $msg)
501    };
502}
503
504/// Early return with an error created from a message.
505///
506/// This macro combines error creation with immediate return, providing a
507/// convenient way to exit functions early when error conditions are detected.
508/// It's equivalent to `return Err(msg_error_anyhow!(message))` but more concise.
509///
510/// ## Early Return Pattern
511///
512/// - **Error Creation**: Creates an `anyhow::Error` with āŒ prefix
513/// - **Immediate Return**: Returns the error immediately from the function
514/// - **Function Exit**: Stops execution at the point of the macro call
515/// - **Clean Code**: Reduces boilerplate for error handling
516///
517/// ## Use Cases
518///
519/// ### Input Validation
520/// ```rust
521/// use anyhow::Result;
522/// use kasl::{msg_bail_anyhow, libs::messages::Message};
523///
524/// fn process_task(task_id: Option<i32>) -> Result<()> {
525///     let id = match task_id {
526///         Some(id) => id,
527///         None => msg_bail_anyhow!(Message::InvalidInput),
528///     };
529///
530///     // Continue processing with valid ID
531///     let _ = id;
532///     Ok(())
533/// }
534/// ```
535///
536/// ### Permission Checking
537/// ```rust
538/// use anyhow::Result;
539/// use kasl::{msg_bail_anyhow, libs::messages::Message};
540///
541/// # fn user_has_permission() -> bool { true }
542/// fn secure_operation() -> Result<()> {
543///     if !user_has_permission() {
544///         msg_bail_anyhow!(Message::PermissionDenied);
545///     }
546///
547///     // Continue with authorized operation
548///     Ok(())
549/// }
550/// ```
551///
552/// ### Resource Validation
553/// ```rust
554/// use anyhow::Result;
555/// use kasl::{msg_bail_anyhow, libs::messages::Message};
556///
557/// # fn resource_exists(_path: &str) -> bool { true }
558/// fn access_resource(path: &str) -> Result<()> {
559///     if !resource_exists(path) {
560///         msg_bail_anyhow!(Message::FileNotFound);
561///     }
562///
563///     // Continue with valid resource
564///     Ok(())
565/// }
566/// ```
567///
568/// ## Code Style Benefits
569///
570/// - **Reduced Boilerplate**: Eliminates repetitive error handling code
571/// - **Clear Intent**: Makes error conditions immediately obvious
572/// - **Consistent Errors**: All bail errors have consistent formatting
573/// - **Maintainability**: Easier to update error handling patterns
574#[macro_export]
575macro_rules! msg_bail_anyhow {
576    ($msg:expr) => {
577        anyhow::bail!("āŒ {}", $msg)
578    };
579}