use std::fmt;
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum ProblemKind {
NotFinite,
Negative,
NotPositive,
OutOfRange {
min: f32,
max: f32,
},
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct Problem {
pub field: &'static str,
pub kind: ProblemKind,
}
impl fmt::Display for Problem {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self.kind {
ProblemKind::NotFinite => write!(f, "`{}` must be a finite number", self.field),
ProblemKind::Negative => write!(f, "`{}` must not be negative", self.field),
ProblemKind::NotPositive => write!(f, "`{}` must be greater than zero", self.field),
ProblemKind::OutOfRange { min, max } => {
write!(f, "`{}` must be between {min} and {max}", self.field)
}
}
}
}
impl std::error::Error for Problem {}
pub fn non_negative(out: &mut Vec<Problem>, field: &'static str, v: f32) {
if !v.is_finite() {
out.push(Problem { field, kind: ProblemKind::NotFinite });
} else if v < 0.0 {
out.push(Problem { field, kind: ProblemKind::Negative });
}
}
pub fn positive(out: &mut Vec<Problem>, field: &'static str, v: f32) {
if !v.is_finite() {
out.push(Problem { field, kind: ProblemKind::NotFinite });
} else if v <= 0.0 {
out.push(Problem { field, kind: ProblemKind::NotPositive });
}
}
pub fn finite(out: &mut Vec<Problem>, field: &'static str, v: f32) {
if !v.is_finite() {
out.push(Problem { field, kind: ProblemKind::NotFinite });
}
}
pub fn within(out: &mut Vec<Problem>, field: &'static str, v: f32, min: f32, max: f32) {
if !v.is_finite() {
out.push(Problem { field, kind: ProblemKind::NotFinite });
} else if v < min || v > max {
out.push(Problem { field, kind: ProblemKind::OutOfRange { min, max } });
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn nan_is_not_finite_and_the_message_names_the_field() {
let mut out = Vec::new();
positive(&mut out, "reach", f32::NAN);
assert_eq!(out, [Problem { field: "reach", kind: ProblemKind::NotFinite }]);
assert_eq!(out[0].to_string(), "`reach` must be a finite number");
}
}