nautalid 0.1.0

Scratch container substrate — TLS 1.3 HTTP/2+3 kernel, LID/AetherDB, optional filter bus (GPL-3.0-or-later).
use std::sync::{Once, OnceLock};

use tracing_subscriber::EnvFilter;
use tracing_subscriber::fmt;
use tracing_subscriber::prelude::*;
use tracing_subscriber::reload;

type SetLogFn = Box<dyn Fn(&str) -> Result<(), String> + Send + Sync>;

static SET_LOG: OnceLock<SetLogFn> = OnceLock::new();
static TRACING_INIT: Once = Once::new();

pub fn init_tracing() {
    TRACING_INIT.call_once(|| {
        let filter =
            EnvFilter::try_from_default_env().unwrap_or_else(|_| EnvFilter::new("info,nautalid=debug"));
        let (filter, handle) = reload::Layer::new(filter);
        let _ = SET_LOG.set(Box::new(move |directive: &str| {
            let next = EnvFilter::try_new(directive).map_err(|e| e.to_string())?;
            handle
                .modify(|current| *current = next)
                .map_err(|e| e.to_string())
        }));

        tracing_subscriber::registry()
            .with(filter)
            .with(fmt::layer().with_target(true))
            .init();
    });
}

/// Startup directive ( **`RUST_LOG`** or default).
#[must_use]
pub fn current_filter_directive() -> String {
    std::env::var("RUST_LOG").unwrap_or_else(|_| "info,nautalid=debug".into())
}

const MAX_LOG_DIRECTIVE_LEN: usize = 256;

/// Apply a new **`EnvFilter`** directive at runtime (bus **`telemetry set-level`**).
pub fn set_filter_directive(directive: &str) -> Result<(), String> {
    if directive.len() > MAX_LOG_DIRECTIVE_LEN {
        return Err(format!(
            "directive exceeds {MAX_LOG_DIRECTIVE_LEN} characters"
        ));
    }
    validate_log_directive(directive)?;
    SET_LOG
        .get()
        .ok_or_else(|| String::from("tracing reload handle not installed"))?(directive)
}

fn validate_log_directive(directive: &str) -> Result<(), String> {
    const LEVELS: &[&str] = &["trace", "debug", "info", "warn", "error", "off"];
    if LEVELS.contains(&directive) {
        return Ok(());
    }
    if let Some(level) = directive.strip_prefix("nautalid=") {
        if LEVELS.contains(&level) {
            return Ok(());
        }
    }
    Err("directive must be trace|debug|info|warn|error|off or nautalid=<level>".into())
}

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

    static INIT: Once = Once::new();

    #[test]
    fn rejects_broad_crate_directive() {
        assert!(validate_log_directive("trace,hickory=debug").is_err());
    }

    #[test]
    fn accepts_nautalid_level() {
        assert!(validate_log_directive("nautalid=debug").is_ok());
    }

    #[test]
    fn set_filter_directive_runtime() {
        INIT.call_once(init_tracing);
        set_filter_directive("warn").expect("reload");
    }
}

#[cfg(feature = "filter-control")]
pub mod control;