arcly-http 0.1.0

Enterprise-grade NestJS-inspired web framework on axum: zero-lock DI, declarative controllers, multi-tenant data routing, transactional outbox, ABAC, and a self-documenting OpenAPI surface
Documentation
//! Zero-allocation trace propagation, span-ID generation, and atomic RED counters.
//!
//! ## W3C Trace Context
//!
//! `parse_traceparent` extracts an incoming `traceparent` header into a
//! `TraceCtx` (trace ID + parent span ID). `new_span_id` / `new_trace_id`
//! generate fresh identifiers for root spans and per-hop server spans.
//!
//! ## Counters
//!
//! Two relaxed atomics track total and in-flight requests. The `RequestGuard`
//! RAII type decrements in-flight on drop so the count stays accurate even when
//! a handler panics.

use std::sync::atomic::{AtomicU64, Ordering::Relaxed};
use std::sync::OnceLock;
use std::time::Instant;

#[derive(Clone, Copy)]
pub struct TraceCtx {
    pub trace_id: [u8; 16],
    pub span_id: [u8; 8],
}

static REQS: AtomicU64 = AtomicU64::new(0);
static INFLT: AtomicU64 = AtomicU64::new(0);

// ─── W3C traceparent parser ─────────────────────────────────────────────────

/// Parse a W3C `traceparent` header without heap allocation.
/// Format: `00-<32 hex trace>-<16 hex span>-<2 hex flags>` (55 bytes minimum).
#[inline]
pub fn parse_traceparent(h: &[u8]) -> Option<TraceCtx> {
    if h.len() < 55 || h[2] != b'-' || h[35] != b'-' || h[52] != b'-' {
        return None;
    }
    let mut tid = [0u8; 16];
    let mut sid = [0u8; 8];
    hex_into(&h[3..35], &mut tid)?;
    hex_into(&h[36..52], &mut sid)?;
    Some(TraceCtx {
        trace_id: tid,
        span_id: sid,
    })
}

#[inline]
fn hex_into(src: &[u8], dst: &mut [u8]) -> Option<()> {
    for (i, byte) in dst.iter_mut().enumerate() {
        *byte = (nyb(src[2 * i])? << 4) | nyb(src[2 * i + 1])?;
    }
    Some(())
}

#[inline]
fn nyb(c: u8) -> Option<u8> {
    match c {
        b'0'..=b'9' => Some(c - b'0'),
        b'a'..=b'f' => Some(c - b'a' + 10),
        b'A'..=b'F' => Some(c - b'A' + 10),
        _ => None,
    }
}

// ─── Span / trace ID generation ─────────────────────────────────────────────

/// Monotonic process-start epoch, initialised once.
fn monotonic_nanos() -> u64 {
    static EPOCH: OnceLock<Instant> = OnceLock::new();
    EPOCH.get_or_init(Instant::now).elapsed().as_nanos() as u64
}

/// Generate a fresh 64-bit span ID using monotonic time + sequential counter.
///
/// Uses xorshift64 mixing so consecutive IDs are not trivially sequential.
/// Not cryptographically secure -- sufficient for trace correlation.
pub fn new_span_id() -> [u8; 8] {
    static COUNTER: AtomicU64 = AtomicU64::new(1);
    let count = COUNTER.fetch_add(1, Relaxed);
    let mut v = monotonic_nanos() ^ count.wrapping_mul(0x9E3779B97F4A7C15);
    v ^= v << 13;
    v ^= v >> 7;
    v ^= v << 17;
    v.to_ne_bytes()
}

/// Generate a fresh 128-bit trace ID (two independent 64-bit halves).
pub fn new_trace_id() -> [u8; 16] {
    let lo = new_span_id();
    let hi = new_span_id();
    let mut out = [0u8; 16];
    out[..8].copy_from_slice(&hi);
    out[8..].copy_from_slice(&lo);
    out
}

// ─── Hex encoding (no external dep) ─────────────────────────────────────────

/// Encode arbitrary bytes as a lowercase hex string.
pub fn hex_encode(bytes: &[u8]) -> String {
    bytes.iter().map(|b| format!("{b:02x}")).collect()
}

// ─── Request counters ────────────────────────────────────────────────────────

/// RAII guard: increments in-flight on construction, decrements on drop.
pub struct RequestGuard;

#[inline]
pub fn on_request_start() -> RequestGuard {
    REQS.fetch_add(1, Relaxed);
    INFLT.fetch_add(1, Relaxed);
    RequestGuard
}

impl Drop for RequestGuard {
    #[inline]
    fn drop(&mut self) {
        INFLT.fetch_sub(1, Relaxed);
    }
}

/// `(total_requests, in_flight)` -- relaxed read, suitable for metrics endpoints.
#[inline]
pub fn snapshot() -> (u64, u64) {
    (REQS.load(Relaxed), INFLT.load(Relaxed))
}