#![doc = include_str!("../README.md")]
#![no_std]
#[cfg(feature = "decode")]
extern crate std;
#[cfg(feature = "decode")]
pub mod decode;
mod place;
pub use pattern_emit::{Pattern, Unusable};
pub use place::{under, Trail};
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub enum Reason {
Absent,
Range,
Duplicate,
Cast,
}
impl Reason {
pub const fn name(self) -> &'static str {
match self {
Reason::Absent => "absent",
Reason::Range => "range",
Reason::Duplicate => "duplicate",
Reason::Cast => "cast",
}
}
}
pub trait Report {
fn fail(&mut self, path: &'static str, at: &[u32], reason: Reason);
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct Failure {
pub path: &'static str,
pub at: [u32; DEPTH],
pub reason: Reason,
}
pub const DEPTH: usize = 4;
#[derive(Clone, Copy, Debug)]
pub struct Failures<const N: usize> {
list: [Option<Failure>; N],
len: usize,
pub dropped: u32,
}
impl<const N: usize> Default for Failures<N> {
fn default() -> Self {
Failures {
list: [None; N],
len: 0,
dropped: 0,
}
}
}
impl<const N: usize> Failures<N> {
pub fn iter(&self) -> impl Iterator<Item = &Failure> {
self.list.iter().flatten()
}
pub fn is_empty(&self) -> bool {
self.len == 0 && self.dropped == 0
}
pub fn len(&self) -> usize {
self.len
}
}
impl<const N: usize> Report for Failures<N> {
fn fail(&mut self, path: &'static str, at: &[u32], reason: Reason) {
let mut kept = [0; DEPTH];
for (k, a) in kept.iter_mut().zip(at) {
*k = *a;
}
match self.list.get_mut(self.len) {
Some(slot) => {
*slot = Some(Failure {
path,
at: kept,
reason,
});
self.len = self.len.saturating_add(1);
}
None => self.dropped = self.dropped.saturating_add(1),
}
}
}
#[derive(Clone, Copy, Debug, Default)]
pub struct Holds {
failed: bool,
}
impl Holds {
pub fn held(&self) -> bool {
!self.failed
}
}
impl Report for Holds {
fn fail(&mut self, _: &'static str, _: &[u32], _: Reason) {
self.failed = true;
}
}
#[derive(Clone, Copy, Debug)]
pub struct Under {
path: &'static str,
pub reason: Option<Reason>,
}
impl Under {
pub const fn new(path: &'static str) -> Under {
Under { path, reason: None }
}
}
impl Report for Under {
fn fail(&mut self, path: &'static str, at: &[u32], reason: Reason) {
if self.reason.is_none() && under(self.path, path, at) {
self.reason = Some(reason);
}
}
}
pub fn multiple_of(x: f64, m: f64) -> bool {
let q = x / m;
q.is_finite() && q == trunc(q)
}
fn trunc(x: f64) -> f64 {
const WHOLE: f64 = 4_503_599_627_370_496.0;
if x.is_nan() || x.abs() >= WHOLE {
return x;
}
#[allow(clippy::cast_possible_truncation)]
let t = x as i64;
t as f64
}
pub fn chars(s: &str) -> u64 {
s.chars().fold(0u64, |n, _| n.saturating_add(1))
}
#[cfg(test)]
mod tests;