#![forbid(unsafe_code)]

extern crate alloc;

use crate::drbg::{Error as DrbgError, HmacDrbg};
use alloc::vec;
use core::num::NonZeroU32;
use rand_core::{CryptoRng, Error as RandError, RngCore};

fn map_error(err: DrbgError) -> RandError {
    let code = match err {
        DrbgError::RequestTooLarge => rand_core::Error::CUSTOM_START,
        DrbgError::ReseedRequired => rand_core::Error::CUSTOM_START + 1,
        DrbgError::EntropyUnavailable => rand_core::Error::CUSTOM_START + 2,
        DrbgError::EntropyHealthFailed => rand_core::Error::CUSTOM_START + 3,
    };
    let code = NonZeroU32::new(code).expect("custom error codes are non-zero");
    RandError::from(code)
}

/// Adapter that exposes [`HmacDrbg`] as a `rand_core` RNG.
pub struct DrbgRng<'a> {
    drbg: &'a mut HmacDrbg,
}

impl<'a> DrbgRng<'a> {
    pub fn new(drbg: &'a mut HmacDrbg) -> Self {
        Self { drbg }
    }
}

impl<'a> RngCore for DrbgRng<'a> {
    fn next_u32(&mut self) -> u32 {
        let mut buf = [0u8; 4];
        self.fill_bytes(&mut buf);
        u32::from_le_bytes(buf)
    }

    fn next_u64(&mut self) -> u64 {
        let mut buf = [0u8; 8];
        self.fill_bytes(&mut buf);
        u64::from_le_bytes(buf)
    }

    fn fill_bytes(&mut self, dest: &mut [u8]) {
        let _ = self.try_fill_bytes(dest);
    }

    fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), RandError> {
        if dest.is_empty() {
            return Ok(());
        }
        let mut tmp = vec![0u8; dest.len()];
        self.drbg.generate(&mut tmp, None).map_err(map_error)?;
        dest.copy_from_slice(&tmp);
        Ok(())
    }
}

impl<'a> CryptoRng for DrbgRng<'a> {}
