use core::{fmt, ops::BitOr};
use bitflags::bitflags;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
#[must_use = "Proof is a flag on Record data, it should be interrogated before using a record"]
#[derive(Copy, Clone, Debug, Default, Eq, PartialEq, Ord, PartialOrd)]
#[repr(u8)]
pub enum Proof {
Secure = 3,
Insecure = 2,
Bogus = 1,
#[default]
Indeterminate = 0,
}
impl Proof {
#[inline]
pub fn is_secure(&self) -> bool {
*self == Self::Secure
}
#[inline]
pub fn is_insecure(&self) -> bool {
*self == Self::Insecure
}
#[inline]
pub fn is_bogus(&self) -> bool {
*self == Self::Bogus
}
#[inline]
pub fn is_indeterminate(&self) -> bool {
*self == Self::Indeterminate
}
}
impl fmt::Display for Proof {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let s = match self {
Self::Secure => "Secure",
Self::Insecure => "Insecure",
Self::Bogus => "Bogus",
Self::Indeterminate => "Indeterminate",
};
f.write_str(s)
}
}
impl core::error::Error for Proof {}
#[test]
fn test_order() {
assert!(Proof::Secure > Proof::Insecure);
assert!(Proof::Insecure > Proof::Bogus);
assert!(Proof::Bogus > Proof::Indeterminate);
}
bitflags! {
pub struct ProofFlags: u32 {
const SECURE = 1 << Proof::Secure as u8;
const INSECURE = 1 << Proof::Insecure as u8;
const BOGUS = 1 << Proof::Bogus as u8;
const INDETERMINATE = 1 << Proof::Indeterminate as u8;
}
}
impl From<Proof> for ProofFlags {
fn from(proof: Proof) -> Self {
match proof {
Proof::Secure => Self::SECURE,
Proof::Insecure => Self::INSECURE,
Proof::Bogus => Self::BOGUS,
Proof::Indeterminate => Self::INDETERMINATE,
}
}
}
impl BitOr for Proof {
type Output = ProofFlags;
fn bitor(self, rhs: Self) -> Self::Output {
ProofFlags::from(self) | ProofFlags::from(rhs)
}
}
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct Proven<T> {
proof: Proof,
value: T,
}
impl<T> Proven<T> {
pub fn new(proof: Proof, value: T) -> Self {
Self { proof, value }
}
pub fn proof(&self) -> Proof {
self.proof
}
pub fn require_as_ref<I: Into<ProofFlags>>(&self, flags: I) -> Result<&T, Proof> {
if flags.into().contains(ProofFlags::from(self.proof)) {
Ok(&self.value)
} else {
Err(self.proof)
}
}
pub fn require<I: Into<ProofFlags>>(self, flags: I) -> Result<T, Self> {
if flags.into().contains(ProofFlags::from(self.proof)) {
Ok(self.value)
} else {
Err(self)
}
}
pub fn map<U, F>(self, f: F) -> Proven<U>
where
F: FnOnce(T) -> U,
{
Proven {
proof: self.proof,
value: f(self.value),
}
}
pub fn into_parts(self) -> (Proof, T) {
let Self { proof, value } = self;
(proof, value)
}
}
impl<T> Proven<Option<T>> {
pub fn transpose(self) -> Option<Proven<T>> {
Some(Proven {
proof: self.proof,
value: self.value?,
})
}
}