use serde::{Deserialize, Serialize};
use std::fmt;
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
pub enum Ternary {
False,
Unknown,
True,
}
impl Ternary {
pub const FALSE: Self = Self::False;
pub const UNKNOWN: Self = Self::Unknown;
pub const TRUE: Self = Self::True;
pub const ALL: [Self; 3] = [Self::False, Self::Unknown, Self::True];
pub const fn from_bool(value: bool) -> Self {
if value { Self::True } else { Self::False }
}
pub const fn to_bool(self) -> Option<bool> {
match self {
Self::True => Some(true),
Self::False => Some(false),
Self::Unknown => None,
}
}
pub const fn is_binary(self) -> bool {
!matches!(self, Self::Unknown)
}
pub const fn as_f64(self) -> f64 {
match self {
Self::False => 0.0,
Self::Unknown => 0.5,
Self::True => 1.0,
}
}
pub const fn is_false(self) -> bool {
matches!(self, Self::False)
}
pub const fn is_true(self) -> bool {
matches!(self, Self::True)
}
pub const fn is_unknown(self) -> bool {
matches!(self, Self::Unknown)
}
pub const fn is_known(self) -> bool {
!matches!(self, Self::Unknown)
}
pub const fn to_bool_strict(self) -> bool {
matches!(self, Self::True)
}
pub const fn to_bool_loose(self) -> bool {
!matches!(self, Self::False)
}
pub fn parse_char(c: char) -> Option<Self> {
match c {
'0' | 'f' | 'F' => Some(Self::False),
'1' | 't' | 'T' => Some(Self::True),
'x' | 'X' | '?' => Some(Self::Unknown),
_ => None,
}
}
pub const fn as_char(self) -> char {
match self {
Self::False => '0',
Self::Unknown => 'X',
Self::True => '1',
}
}
pub const fn as_compact_char(self) -> char {
match self {
Self::False => '0',
Self::Unknown => 'x',
Self::True => '1',
}
}
}
impl fmt::Display for Ternary {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(match self {
Self::False => "0",
Self::Unknown => "X",
Self::True => "1",
})
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn ordering_is_false_unknown_true() {
assert!(Ternary::False < Ternary::Unknown);
assert!(Ternary::Unknown < Ternary::True);
}
#[test]
fn parse_char_round_trips() {
for v in Ternary::ALL {
assert_eq!(Ternary::parse_char(v.as_char()), Some(v));
}
assert_eq!(Ternary::parse_char('?'), Some(Ternary::Unknown));
assert_eq!(Ternary::parse_char('z'), None);
}
#[test]
fn numeric_mapping() {
assert!((Ternary::False.as_f64() - 0.0).abs() < f64::EPSILON);
assert!((Ternary::Unknown.as_f64() - 0.5).abs() < f64::EPSILON);
assert!((Ternary::True.as_f64() - 1.0).abs() < f64::EPSILON);
}
#[test]
fn binarization() {
assert!(!Ternary::Unknown.to_bool_strict());
assert!(Ternary::Unknown.to_bool_loose());
assert!(Ternary::True.to_bool_strict());
assert!(Ternary::False.is_known());
assert!(!Ternary::Unknown.is_known());
}
}