use std::{fmt, str::FromStr};
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum Termination {
Unwind,
Abort,
}
#[derive(
Debug,
Clone,
Copy,
PartialEq,
Eq,
Hash,
PartialOrd,
Ord,
Serialize,
Deserialize,
)]
#[repr(u8)]
pub enum Category {
Index = 0,
Overflow = 1,
DivideByZero = 2,
RemainderByZero = 3,
Unwrap = 4,
Explicit = 5,
StrBoundary = 6,
Borrow = 7,
Poison = 8,
CapacityOverflow = 9,
AllocFailure = 10,
RefCountOverflow = 11,
Fmt = 12,
NullDeref = 13,
MisalignedRef = 14,
Unknown = 15,
UbCheck = 16,
Foreign = 17,
DynCall = 18,
FnPointer = 19,
GenericBound = 20,
}
pub const ALL: [Category; 21] = [
Category::Index,
Category::Overflow,
Category::DivideByZero,
Category::RemainderByZero,
Category::Unwrap,
Category::Explicit,
Category::StrBoundary,
Category::Borrow,
Category::Poison,
Category::CapacityOverflow,
Category::AllocFailure,
Category::RefCountOverflow,
Category::Fmt,
Category::NullDeref,
Category::MisalignedRef,
Category::Unknown,
Category::UbCheck,
Category::Foreign,
Category::DynCall,
Category::FnPointer,
Category::GenericBound,
];
impl Category {
#[must_use]
pub const fn name(self) -> &'static str {
match self {
Self::Index => "index",
Self::Overflow => "overflow",
Self::DivideByZero => "divide-by-zero",
Self::RemainderByZero => "remainder-by-zero",
Self::Unwrap => "unwrap",
Self::Explicit => "explicit",
Self::StrBoundary => "str-boundary",
Self::Borrow => "borrow",
Self::Poison => "poison",
Self::CapacityOverflow => "capacity-overflow",
Self::AllocFailure => "alloc-failure",
Self::RefCountOverflow => "refcount-overflow",
Self::Fmt => "fmt",
Self::NullDeref => "null-deref",
Self::MisalignedRef => "misaligned-ref",
Self::Unknown => "unknown",
Self::UbCheck => "ub-check",
Self::Foreign => "foreign",
Self::DynCall => "dyn-call",
Self::FnPointer => "fn-pointer",
Self::GenericBound => "generic-bound",
}
}
#[must_use]
pub const fn describe(self) -> &'static str {
match self {
Self::Index => "slice or array index out of bounds",
Self::Overflow => "arithmetic overflow",
Self::DivideByZero => "integer division by zero",
Self::RemainderByZero => "integer remainder by zero",
Self::Unwrap => "unwrap or expect on a None or Err value",
Self::Explicit => "panic!, assert!, unreachable!, or todo!",
Self::StrBoundary => "str sliced at a non-character boundary",
Self::Borrow => "RefCell borrow conflict",
Self::Poison => "poisoned Mutex or RwLock",
Self::CapacityOverflow => "collection capacity overflow",
Self::AllocFailure => "allocator could not satisfy a request",
Self::RefCountOverflow => "Rc or Arc strong count overflow",
Self::Fmt => "panic from the formatting machinery",
Self::NullDeref => "null pointer dereference",
Self::MisalignedRef => "reference from a misaligned pointer",
Self::Unknown => "unclassified panic",
Self::UbCheck => "standard library precondition check",
Self::Foreign => "call into foreign code, which has no Rust body",
Self::DynCall => "dyn trait call with an unresolved target set",
Self::FnPointer => "call through a function pointer",
Self::GenericBound => {
"call decided by a caller's choice of generic arguments"
}
}
}
#[must_use]
pub const fn bit(self) -> u32 {
1u32 << (self as u8)
}
}
impl fmt::Display for Category {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(self.name())
}
}
impl FromStr for Category {
type Err = ();
fn from_str(s: &str) -> Result<Self, Self::Err> {
ALL.into_iter().find(|c| c.name() == s).ok_or(())
}
}
#[derive(
Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize,
)]
pub struct CategorySet(u32);
impl CategorySet {
pub const EMPTY: Self = Self(0);
#[must_use]
pub const fn oom() -> Self {
Self(Category::CapacityOverflow.bit() | Category::AllocFailure.bit())
}
#[must_use]
pub const fn default_suppressed() -> Self {
Self(Self::oom().0 | Category::UbCheck.bit())
}
#[must_use]
pub const fn assumed() -> Self {
Self(
Category::Unknown.bit()
| Category::Foreign.bit()
| Category::DynCall.bit()
| Category::FnPointer.bit()
| Category::GenericBound.bit(),
)
}
#[must_use]
pub const fn single(c: Category) -> Self {
Self(c.bit())
}
pub const fn insert(&mut self, c: Category) {
self.0 |= c.bit();
}
#[must_use]
pub const fn contains(self, c: Category) -> bool {
self.0 & c.bit() != 0
}
#[must_use]
pub const fn is_empty(self) -> bool {
self.0 == 0
}
#[must_use]
pub const fn union(self, other: Self) -> Self {
Self(self.0 | other.0)
}
#[must_use]
pub const fn difference(self, other: Self) -> Self {
Self(self.0 & !other.0)
}
#[must_use]
pub const fn intersection(self, other: Self) -> Self {
Self(self.0 & other.0)
}
#[must_use]
pub const fn contains_all(self, other: Self) -> bool {
self.0 & other.0 == other.0
}
#[must_use]
pub const fn len(self) -> u32 {
self.0.count_ones()
}
pub fn iter(self) -> impl Iterator<Item = Category> {
ALL.into_iter().filter(move |c| self.contains(*c))
}
#[must_use]
pub fn names(self) -> Vec<&'static str> {
self.iter().map(Category::name).collect()
}
}
impl FromIterator<Category> for CategorySet {
fn from_iter<I: IntoIterator<Item = Category>>(iter: I) -> Self {
let mut set = Self::EMPTY;
for c in iter {
set.insert(c);
}
set
}
}
impl fmt::Display for CategorySet {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
for (i, c) in self.iter().enumerate() {
if i > 0 {
f.write_str(", ")?;
}
write!(f, "{c}")?;
}
Ok(())
}
}
pub fn parse_selector(s: &str) -> Result<CategorySet, String> {
let mut set = CategorySet::EMPTY;
for tok in s.split(',').map(str::trim).filter(|t| !t.is_empty()) {
match tok {
"oom" => set = set.union(CategorySet::oom()),
"assumed" => set = set.union(CategorySet::assumed()),
"default" => {
set = set.union(CategorySet::default_suppressed());
}
"all" => set = ALL.into_iter().collect(),
other => match other.parse::<Category>() {
Ok(c) => set.insert(c),
Err(()) => return Err(other.to_owned()),
},
}
}
Ok(set)
}