use alibi_core::{AuthError, AuthResult};
use chrono::{DateTime, Utc};
use num_traits::ToPrimitive;
use rand::RngExt;
pub(super) fn generate_code(length: f64) -> AuthResult<String> {
if length.is_nan() {
return Ok(String::new());
}
if length <= 0.0 {
return Err(AuthError::internal("Length must be a positive integer."));
}
if !(0.5..32768.5).contains(&length) {
return Err(AuthError::internal("Unsupported random code length"));
}
let count = length
.ceil()
.to_u32()
.ok_or_else(|| AuthError::internal("Unsupported random code length"))?;
let mut rng = rand::rng();
Ok((0..count)
.map(|_| char::from(b'0' + rng.random_range(0..10)))
.collect())
}
pub(super) fn expires_at(seconds: f64, zero_nan_default: bool) -> Option<DateTime<Utc>> {
let now = Utc::now();
let seconds = if zero_nan_default && (seconds == 0.0 || seconds.is_nan()) {
300.0
} else {
seconds
};
let milliseconds = now.timestamp_millis().to_f64()? + seconds * 1000.0;
if !milliseconds.is_finite() || milliseconds.abs() > 8_640_000_000_000_000.0 {
return None;
}
let milliseconds = milliseconds.trunc().to_i64()?;
DateTime::from_timestamp_millis(milliseconds)
}
pub(super) fn attempts_number(attempts: usize) -> f64 {
attempts.to_f64().unwrap_or(f64::INFINITY)
}