endpoint-libs 3.4.3

Launch MCP services fast: describe endpoints once in RON, and endpoint-gen generates the Rust models, docs and MCP tool schemas that this crate serves over WebSocket RPC, with roles and typed errors built in.
Documentation
use eyre::Result;

pub fn get_log_id() -> u64 {
    chrono::Utc::now().timestamp_micros() as _
}

/// A connection id unique within this process.
///
/// It was the clock in microseconds, cut to 32 bits, which gives two
/// connections accepted in the same microsecond the same id. With one accept
/// loop that took two accepts inside a microsecond; with a shard per core it
/// is two shards accepting at once, and the second insert into the shared
/// connection table replaced the first. A counter cannot repeat until it wraps.
/// It starts from the clock so ids still differ across restarts in logs.
pub fn get_conn_id() -> u32 {
    use std::sync::OnceLock;
    use std::sync::atomic::{AtomicU32, Ordering};
    static NEXT: OnceLock<AtomicU32> = OnceLock::new();
    NEXT.get_or_init(|| AtomicU32::new(chrono::Utc::now().timestamp_micros() as u32))
        .fetch_add(1, Ordering::Relaxed)
}

#[cfg(test)]
mod conn_id_tests {
    #[test]
    fn ids_from_many_threads_never_repeat() {
        let ids: Vec<u32> = std::thread::scope(|scope| {
            let workers: Vec<_> = (0..8)
                .map(|_| {
                    scope.spawn(|| {
                        (0..10_000)
                            .map(|_| super::get_conn_id())
                            .collect::<Vec<_>>()
                    })
                })
                .collect();
            workers
                .into_iter()
                .flat_map(|worker| worker.join().unwrap())
                .collect()
        });
        let unique: std::collections::HashSet<u32> = ids.iter().copied().collect();
        assert_eq!(unique.len(), ids.len());
    }
}

pub fn get_time_milliseconds() -> i64 {
    chrono::Utc::now().timestamp_millis()
}
pub fn get_time_micros() -> i64 {
    chrono::Utc::now().timestamp_micros()
}
pub fn hex_decode(s: &[u8]) -> Result<Vec<u8>> {
    if s.starts_with(b"0x") {
        Ok(hex::decode(&s[2..])?)
    } else {
        Ok(hex::decode(s)?)
    }
}

/// Aligns the precision of one `f64` value to match another `f64` value.
pub fn align_precision(a: f64, b: f64) -> f64 {
    let precision_b = count_dp(b);
    let precision_a = format!("{:.0$}", { precision_b });
    let aligned_a = format!("{:.*}", precision_a.parse::<usize>().unwrap(), a);
    aligned_a.parse().unwrap()
}

pub fn count_dp(num: f64) -> usize {
    // Convert the f64 to a string representation
    let num_str = format!("{num}");

    // Find the position of the decimal point
    if let Some(decimal_index) = num_str.find('.') {
        // Count the number of characters after the decimal point
        num_str.len() - decimal_index - 1
    } else {
        // If there is no decimal point, return 0
        0
    }
}

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

    #[test]
    fn test_count_dp() {
        assert_eq!(count_dp(1.5), 1);
        assert_eq!(count_dp(1.1), 1);
    }

    #[test]
    fn test_align_precision() {
        let a = 123.456789;
        let b = 78.9;
        let aligned_a = align_precision(a, b);
        assert_eq!(aligned_a, 123.5);

        let a = 2.37;
        let b = 631.3;
        let aligned_a = align_precision(a, b);
        assert_eq!(aligned_a, 2.4);
    }
}