pricelevel 0.7.0

A high-performance, lock-free price level implementation for limit order books in Rust. This library provides the building blocks for creating efficient trading systems with support for multiple order types and concurrent access patterns.
Documentation
//! # Utils Module
//!
//! This module provides a collection of utility functions, structures, and tools designed to simplify and support
//! common tasks across the library. These utilities range from logging, time frame management, testing helpers,
//! and other general-purpose helpers.
//!
//! ## Key Components
//!
//! ### Logger (`logger`)
//!
//! Handles application logging with configurable log levels. It includes safe and idempotent initialization to avoid
//! redundant setups. Useful for debugging, tracing, and monitoring program behavior.
//!
//! **Log Levels:**
//! - `DEBUG`: Detailed debugging information.
//! - `INFO`: General application status information.
//! - `WARN`: Non-critical issues that require attention.
//! - `ERROR`: Significant problems causing failures.
//! - `TRACE`: Fine-grained application execution details.
//!
//! ### Time Frames (`time`)
//!
//! Provides robust structures for managing common time frames used in financial or other periodic systems.
//! This includes predefined constants for standard periods and support for custom periods.
//!
//! **Supported Time Frames:**
//! - Microsecond
//! - Millisecond
//! - Second
//! - Minute
//! - Hour
//! - Day
//! - Week
//! - Month
//! - Quarter
//! - Year
//! - Custom periods (customizable to specific needs)
//!
//! ### Testing Utilities (`tests`)
//!
//! A set of functions and macros to simplify testing. These include utilities for relative equality comparisons
//! and other common test-case behaviors.
//!
//!
//! ### Miscellaneous Utilities (`others`)
//!
//! General-purpose functions for common operations, such as approximate equality checks, random selection from a
//! collection, and iterating over combinations.
//!
//! ## Performance Characteristics
//!
//! - **Logger:** Initialization is thread-safe and happens only once, ensuring minimal performance impact.
//! - **Time Frames:** All operations on time structures are constant-time.
//! - **Random Selection:** Complexity is O(n), where `n` is the size of the collection.
//! - **Combination Processing:** Complexity depends on the size of each combination and the number of combinations processed.
//!
//! ## Design Notes
//!
//! - **Logger:** Leverages the `tracing` crate, enabling structured and asynchronous logging.
//! - **Time Frames:** Focuses on reusable constants while supporting flexible customizations.
//! - **Testing Utilities:** Targets precise and consistent floating-point comparisons to prevent test inaccuracies.
//! - **General Utilities:** Built with error handling, edge case scenarios, and performance in mind.
//!

use crate::errors::PriceLevelError;
use std::sync::OnceLock;

use tracing_subscriber::FmtSubscriber;

use {std::env, tracing::Level};

#[allow(dead_code)]
static LOGGER_INIT_RESULT: OnceLock<Result<(), String>> = OnceLock::new();

/// Sets up a logger for the application
///
/// The logger level is determined by the `LOGLEVEL` environment variable.
/// Supported log levels are:
/// - `DEBUG`: Captures detailed debug information.
/// - `ERROR`: Captures error messages.
/// - `WARN`: Captures warnings.
/// - `TRACE`: Captures detailed trace logs.
/// - All other values default to `INFO`, which captures general information.
///
/// **Behavior:**
/// - Concurrent calls to this function result in the logger being initialized only once.
///
/// # Errors
/// Returns an error if initializing the global subscriber fails.
#[allow(dead_code)]
pub fn setup_logger() -> Result<(), PriceLevelError> {
    let result = LOGGER_INIT_RESULT.get_or_init(|| {
        let log_level = env::var("LOGLEVEL")
            .unwrap_or_else(|_| "INFO".to_string())
            .to_uppercase();

        let level = match log_level.as_str() {
            "DEBUG" => Level::DEBUG,
            "ERROR" => Level::ERROR,
            "WARN" => Level::WARN,
            "TRACE" => Level::TRACE,
            _ => Level::INFO,
        };

        let subscriber = FmtSubscriber::builder().with_max_level(level).finish();

        tracing::subscriber::set_global_default(subscriber)
            .map_err(|error| format!("failed to set global logging subscriber: {error}"))?;

        tracing::debug!("Log level set to: {}", level);

        Ok(())
    });

    result
        .clone()
        .map_err(|message| PriceLevelError::InvalidOperation { message })
}

#[cfg(test)]
mod tests_setup_logger {
    use super::setup_logger;
    use std::env;
    use tracing::subscriber::set_global_default;
    use tracing_subscriber::FmtSubscriber;

    #[test]

    fn test_logger_initialization_info() {
        unsafe {
            env::set_var("LOGLEVEL", "INFO");
        }
        assert!(setup_logger().is_ok());

        assert!(
            set_global_default(FmtSubscriber::builder().finish()).is_err(),
            "Logger should already be set"
        );
    }

    #[test]

    fn test_logger_initialization_debug() {
        unsafe {
            env::set_var("LOGLEVEL", "DEBUG");
        }
        assert!(setup_logger().is_ok());

        assert!(
            set_global_default(FmtSubscriber::builder().finish()).is_err(),
            "Logger should already be set"
        );
    }

    #[test]

    fn test_logger_initialization_default() {
        unsafe {
            env::remove_var("LOGLEVEL");
        }
        assert!(setup_logger().is_ok());

        assert!(
            set_global_default(FmtSubscriber::builder().finish()).is_err(),
            "Logger should already be set"
        );
    }

    #[test]

    fn test_logger_called_once() {
        unsafe {
            env::set_var("LOGLEVEL", "INFO");
        }

        assert!(setup_logger().is_ok()); // First call should set up the logger
        assert!(setup_logger().is_ok()); // Second call should not re-initialize

        assert!(
            set_global_default(FmtSubscriber::builder().finish()).is_err(),
            "Logger should already be set and should not be reset"
        );
    }
}

#[cfg(test)]
mod tests_setup_logger_bis {
    use super::*;
    use std::sync::Mutex;
    use tracing::subscriber::with_default;
    use tracing_subscriber::Layer;
    use tracing_subscriber::layer::{Context, SubscriberExt};
    use tracing_subscriber::registry;

    static TEST_MUTEX: Mutex<()> = Mutex::new(());

    #[derive(Clone)]
    struct TestLayer {
        level: std::sync::Arc<Mutex<Option<Level>>>,
    }

    impl<S> Layer<S> for TestLayer
    where
        S: tracing::Subscriber,
    {
        fn on_event(&self, event: &tracing::Event<'_>, _ctx: Context<'_, S>) {
            let mut level = self.level.lock().unwrap();
            *level = Some(*event.metadata().level());
        }
    }

    fn create_test_layer() -> (TestLayer, std::sync::Arc<Mutex<Option<Level>>>) {
        let level = std::sync::Arc::new(Mutex::new(None));
        (
            TestLayer {
                level: level.clone(),
            },
            level,
        )
    }

    #[test]

    fn test_default_log_level() {
        let _lock = TEST_MUTEX.lock().unwrap();
        unsafe {
            env::remove_var("LOGLEVEL");
        }

        let (layer, level) = create_test_layer();
        let subscriber = registry().with(layer);

        with_default(subscriber, || {
            assert!(setup_logger().is_ok());
            tracing::info!("Test log");
        });

        assert_eq!(*level.lock().unwrap(), Some(Level::INFO));
    }

    #[test]

    fn test_debug_log_level() {
        let _lock = TEST_MUTEX.lock().unwrap();
        unsafe {
            env::set_var("LOGLEVEL", "DEBUG");
        }

        let (layer, level) = create_test_layer();
        let subscriber = registry().with(layer);

        with_default(subscriber, || {
            assert!(setup_logger().is_ok());
            tracing::debug!("Test log");
        });

        assert_eq!(*level.lock().unwrap(), Some(Level::DEBUG));
        unsafe {
            env::remove_var("LOGLEVEL");
        }
    }

    #[test]

    fn test_error_log_level() {
        let _lock = TEST_MUTEX.lock().unwrap();
        unsafe {
            env::set_var("LOGLEVEL", "ERROR");
        }

        let (layer, level) = create_test_layer();
        let subscriber = registry().with(layer);

        with_default(subscriber, || {
            assert!(setup_logger().is_ok());
            tracing::error!("Test log");
        });

        assert_eq!(*level.lock().unwrap(), Some(Level::ERROR));
        unsafe {
            env::remove_var("LOGLEVEL");
        }
    }

    #[test]

    fn test_warn_log_level() {
        let _lock = TEST_MUTEX.lock().unwrap();
        unsafe {
            env::set_var("LOGLEVEL", "WARN");
        }
        let (layer, level) = create_test_layer();
        let subscriber = registry().with(layer);

        with_default(subscriber, || {
            assert!(setup_logger().is_ok());
            tracing::warn!("Test log");
        });

        assert_eq!(*level.lock().unwrap(), Some(Level::WARN));
        unsafe {
            env::remove_var("LOGLEVEL");
        }
    }

    #[test]

    fn test_trace_log_level() {
        let _lock = TEST_MUTEX.lock().unwrap();
        unsafe {
            env::set_var("LOGLEVEL", "TRACE");
        }

        let (layer, level) = create_test_layer();
        let subscriber = registry().with(layer);

        with_default(subscriber, || {
            assert!(setup_logger().is_ok());
            tracing::trace!("Test log");
        });

        assert_eq!(*level.lock().unwrap(), Some(Level::TRACE));

        unsafe {
            env::remove_var("LOGLEVEL");
        }
    }

    #[test]

    fn test_invalid_log_level() {
        let _lock = TEST_MUTEX.lock().unwrap();
        unsafe {
            env::set_var("LOGLEVEL", "INVALID");
        }

        let (layer, level) = create_test_layer();
        let subscriber = registry().with(layer);

        with_default(subscriber, || {
            assert!(setup_logger().is_ok());
            tracing::info!("Test log");
        });

        assert_eq!(*level.lock().unwrap(), Some(Level::INFO));
        unsafe {
            env::remove_var("LOGLEVEL");
        }
    }
}