fake-log 0.1.0

A fake logging implementation with the same interface as log crate but with no-op implementation for embedded scenarios
//! # fake-log
//!
//! A fake logging library that provides the same interface as rust log crate.
//! All log operations are no-op implementations, mainly used to reduce stack memory
//! usage caused by log libraries in embedded scenarios.
//!
//! ## Usage
//!
//! Replace log dependency in Cargo.toml:
//!
//! ```toml
//! [dependencies]
//! # log = "0.4"  # Comment out the original log dependency
//! fake-log = { version = "0.1", package = "log" }
//! ```
//!
//! Then use log macros normally in your code:
//!
//! ```rust
//! use fake_log::{info, debug, error, warn, trace};
//!
//! fn main() {
//!     info!("This is an info log");
//!     debug!("This is a debug log: {}", 42);
//!     error!("This is an error log");
//! }
//! ```
//!
//! All log outputs will be silently ignored, producing no output or memory allocation.

#![no_std]

// Re-export all public types and traits from log crate
// This ensures complete API compatibility
pub use log::{
    Level, LevelFilter, Log, Metadata, MetadataBuilder, ParseLevelError, Record, RecordBuilder,
    SetLoggerError, STATIC_MAX_LEVEL,
};

/// NopLogger - a logger implementation that does nothing
struct NopLogger;

impl Log for NopLogger {
    fn enabled(&self, _metadata: &Metadata) -> bool {
        false // Always return false, indicating no logs are enabled
    }

    fn log(&self, _record: &Record) {
        // No-op implementation - does nothing
    }

    fn flush(&self) {
        // No-op implementation - does nothing
    }
}

/// Get maximum log level (fake implementation)
pub fn max_level() -> LevelFilter {
    LevelFilter::Off // Always return Off, indicating all logs are disabled
}

/// Set maximum log level (fake implementation)
pub fn set_max_level(_level: LevelFilter) {
    // No-op implementation
}

/// Set logger (fake implementation)
pub fn set_logger(_logger: &'static dyn Log) -> Result<(), SetLoggerError> {
    // No-op implementation
    Ok(())
}

/// Set boxed logger (fake implementation)
#[cfg(feature = "std")]
pub fn set_boxed_logger(_logger: Box<dyn Log>) -> Result<(), SetLoggerError> {
    // No-op implementation
    Ok(())
}

/// Non-thread-safe version of set_logger (fake implementation)
pub fn set_logger_racy(_logger: &'static dyn Log) -> Result<(), SetLoggerError> {
    // No-op implementation
    Ok(())
}

/// Non-thread-safe version of set_max_level (fake implementation)
pub fn set_max_level_racy(_level: LevelFilter) {
    // No-op implementation
}

/// Get logger reference (fake implementation)
pub fn logger() -> &'static dyn Log {
    &NopLogger
}

/// Generic log macro - no-op implementation
#[macro_export]
macro_rules! log {
    (target: $target:expr, $lvl:expr, $($arg:tt)+) => {{
        // No-op implementation - ignored at compile time
    }};
    ($lvl:expr, $($arg:tt)+) => {{
        // No-op implementation - ignored at compile time
    }};
}

/// Error level log macro - no-op implementation
#[macro_export]
macro_rules! error {
    (target: $target:expr, $($arg:tt)*) => {{ let _ = format_args!($($arg)*); }};
    ($($arg:tt)*) => {{ let _ = format_args!($($arg)*); }};
}

/// Warn level log macro - no-op implementation
#[macro_export]
macro_rules! warn {
    (target: $target:expr, $($arg:tt)*) => {{ let _ = format_args!($($arg)*); }};
    ($($arg:tt)*) => {{ let _ = format_args!($($arg)*); }};
}

/// Info level log macro - no-op implementation
#[macro_export]
macro_rules! info {
    (target: $target:expr, $($arg:tt)*) => {{ let _ = format_args!($($arg)*); }};
    ($($arg:tt)*) => {{ let _ = format_args!($($arg)*); }};
}

/// Debug level log macro - no-op implementation
#[macro_export]
macro_rules! debug {
    (target: $target:expr, $($arg:tt)*) => {{ let _ = format_args!($($arg)*); }};
    ($($arg:tt)*) => {{ let _ = format_args!($($arg)*); }};
}

/// Trace level log macro - no-op implementation
#[macro_export]
macro_rules! trace {
    (target: $target:expr, $($arg:tt)*) => {{ let _ = format_args!($($arg)*); }};
    ($($arg:tt)*) => {{ let _ = format_args!($($arg)*); }};
}

/// Macro to check if log is enabled - no-op implementation
#[macro_export]
macro_rules! log_enabled {
    (target: $target:expr, $lvl:expr) => {{
        false // Always return false
    }};
    ($lvl:expr) => {{
        false // Always return false
    }};
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_log_macros() {
        // Test that all log macros compile normally and don't panic
        error!("This is an error");
        warn!("This is a warning");
        info!("This is info");
        debug!("This is debug");
        trace!("This is trace");

        error!(target: "test", "This is an error with target");
        warn!(target: "test", "This is a warning with target");
        info!(target: "test", "This is info with target");
        debug!(target: "test", "This is debug with target");
        trace!(target: "test", "This is trace with target");

        // Test formatting parameters
        let x = 42;
        info!("Formatted: {}", x);
        debug!("Multiple: {} {}", x, "test");
    }

    #[test]
    fn test_log_enabled() {
        // Test log_enabled macro
        assert_eq!(log_enabled!(Level::Error), false);
        assert_eq!(log_enabled!(target: "test", Level::Info), false);
    }

    #[test]
    fn test_logger() {
        // Test logger function
        let logger = logger();
        let metadata = Metadata::builder()
            .level(Level::Info)
            .target("test")
            .build();

        assert_eq!(logger.enabled(&metadata), false);

        let record = Record::builder()
            .level(Level::Info)
            .target("test")
            .args(format_args!("test message"))
            .build();

        logger.log(&record); // Should not panic
        logger.flush(); // Should not panic
    }

    #[test]
    fn test_max_level() {
        // Test max_level function
        let level = max_level();
        assert_eq!(level, LevelFilter::Off);

        // Test that set_max_level doesn't panic
        set_max_level(LevelFilter::Debug);
        set_max_level_racy(LevelFilter::Info);
    }

    #[test]
    fn test_set_logger() {
        // Test set_logger function
        static TEST_LOGGER: NopLogger = NopLogger;
        assert!(set_logger(&TEST_LOGGER).is_ok());
        assert!(set_logger_racy(&TEST_LOGGER).is_ok());
    }

    #[test]
    #[cfg(feature = "std")]
    fn test_set_boxed_logger() {
        // Test set_boxed_logger function
        let boxed_logger = Box::new(NopLogger);
        assert!(set_boxed_logger(boxed_logger).is_ok());
    }

    #[test]
    fn test_type_compatibility() {
        // Test type compatibility
        let level = Level::Info;
        let filter = LevelFilter::Debug;

        // These should all work normally, as we use original types from log crate
        assert!(level >= Level::Error); // Info has lower priority than Error
        assert!(filter >= LevelFilter::Info);

        // Test interoperability with log crate - simplified version
        let metadata = Metadata::builder()
            .level(Level::Warn)
            .target("test_target")
            .build();

        assert_eq!(metadata.level(), Level::Warn);
        assert_eq!(metadata.target(), "test_target");
    }
}