Documentation
use core::cell::RefCell;

use crate::unix::DevRandom;

#[cfg(unix)]
pub mod unix;

pub trait RandomSource {
    fn fill_bytes(&mut self, bytes: &mut [u8]);
}

pub trait Random: Sized {
    fn random<S>(source: &mut S) -> Self
    where
        S: RandomSource + ?Sized;
}

impl Random for bool {
    fn random<S>(source: &mut S) -> Self
    where
        S: RandomSource + ?Sized
    {
        u8::random(source) & 1 == 1
    }
}

macro_rules! numerics {
    ($($t:ty),* $(,)?) => {
        $(
            impl Random for $t {
                fn random<S>(source: &mut S) -> Self
                where
                    S: RandomSource + ?Sized
                {
                    let mut bytes = (0 as Self).to_ne_bytes();
                    source.fill_bytes(&mut bytes);
                    Self::from_ne_bytes(bytes)
                }
            }
        )*
    };
}

numerics!(u8, i8, u16, i16, u32, i32, u64, i64, u128, i128, usize, isize);

#[cfg(unix)]
thread_local! {
    static GLOBAL_RAND: RefCell<DevRandom> = RefCell::new(DevRandom::new().unwrap());
}

pub fn random<T: Random>() -> T {
    GLOBAL_RAND.with(|r| {
        let mut source = r.borrow_mut();
        T::random(&mut *source)
    })
}