use const_macros::{const_ok, const_try};
#[cfg(not(feature = "unsafe-length"))]
use const_macros::const_early;
use miette::Diagnostic;
#[cfg(feature = "serde")]
use serde::{de, Deserialize, Deserializer, Serialize, Serializer};
use thiserror::Error;
#[cfg(not(feature = "unsafe-length"))]
use crate::macros::errors;
use crate::algorithm::Algorithm;
pub const DEFAULT: usize = 20;
#[cfg(not(feature = "unsafe-length"))]
pub const MIN: usize = 16;
#[cfg(not(feature = "unsafe-length"))]
#[derive(Debug, Error, Diagnostic)]
#[error("expected length of at least `{MIN}`, got `{length}`")]
#[diagnostic(
code(otp_std::secret::length),
help("make sure the secret length is at least `{MIN}`")
)]
pub struct Error {
pub length: usize,
}
#[cfg(not(feature = "unsafe-length"))]
impl Error {
pub const fn new(length: usize) -> Self {
Self { length }
}
}
#[cfg(feature = "unsafe-length")]
#[derive(Debug, Error, Diagnostic)]
pub enum Error {}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct Length {
value: usize,
}
#[cfg(feature = "serde")]
impl Serialize for Length {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
self.get().serialize(serializer)
}
}
#[cfg(feature = "serde")]
impl<'de> Deserialize<'de> for Length {
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
let value = usize::deserialize(deserializer)?;
Self::new(value).map_err(de::Error::custom)
}
}
impl TryFrom<usize> for Length {
type Error = Error;
fn try_from(value: usize) -> Result<Self, Self::Error> {
Self::new(value)
}
}
impl From<Length> for usize {
fn from(length: Length) -> Self {
length.get()
}
}
impl Default for Length {
fn default() -> Self {
Self::DEFAULT
}
}
#[cfg(not(feature = "unsafe-length"))]
errors! {
Type = Error,
Hack = $,
error => new(length),
}
impl Length {
pub const fn new(value: usize) -> Result<Self, Error> {
const_try!(Self::check(value));
Ok(unsafe { Self::new_unchecked(value) })
}
pub const fn new_ok(value: usize) -> Option<Self> {
const_ok!(Self::new(value))
}
#[allow(unused_variables)]
pub const fn check(value: usize) -> Result<(), Error> {
#[cfg(not(feature = "unsafe-length"))]
const_early!(value < MIN => error!(value));
Ok(())
}
pub const unsafe fn new_unchecked(value: usize) -> Self {
Self { value }
}
pub const fn recommended_for(algorithm: Algorithm) -> Self {
unsafe { Self::new_unchecked(algorithm.recommended_length()) }
}
pub const fn get(self) -> usize {
self.value
}
#[cfg(not(feature = "unsafe-length"))]
pub const MIN: Self = Self::new_ok(MIN).unwrap();
pub const DEFAULT: Self = Self::new_ok(DEFAULT).unwrap();
}