Crate checked_rs
source ·Expand description
§checked-rs
A library for encoding validation semantics into the type system.
§Overview
The main components of this library are the structs View
(plus the Validator
trait), HardClamp
, SoftClamp
and the attribute macro clamped
.
Additionally, there are some traits and types such as Behavior
and ClampGuard
that either configure how overflow is handled or provide an alternative way to interact with the clamped types.
§HardClamp
The HardClamp
struct is a wrapper around an unsigned integer that clamps the value to a specified range.
use checked_rs::prelude::*;
let mut val = HardClamp::<u8, Saturating, 0, 10>::new(5).unwrap();
assert_eq!(val.get(), 5);
val += 5;
assert_eq!(val.get(), 10);
val -= 15;
assert_eq!(val.get(), 0);
val += 20;
assert_eq!(val.get(), 10);
§SoftClamp
The SoftClamp
struct is a wrapper around an unsigned integer that can be checked for if it is within a specified range.
use checked_rs::prelude::*;
let mut val = SoftClamp::<u8, Saturating, 0, 10>::new(5);
assert_eq!(*val, 5);
assert_eq!(val.is_valid(), true);
val += 5;
assert_eq!(*val, 10);
assert_eq!(val.is_valid(), true);
val -= 15;
assert_eq!(*val, 0);
assert_eq!(val.is_valid(), true);
*val = 30;
assert_eq!(*val, 30);
assert_eq!(val.is_valid(), false);
§Behavior
The Behavior
trait is used to configure how overflow is handled for the clamped types.
There are two inherent implementations of Behavior
that can be used: Panicking
and Saturating
.
The default behavior is to panic on overflow.
§ClampGuard
The ClampGuard
struct is a RAII type that is used to modify a clamped value via an exclusive borrow. It will allow the value it tracks to go out of bounds temporarily, but will not allow any value to propagate to the original that is invalid.
This is also useful when you want to change a value temporarily and then revert it back to the original value if the change is not valid or is otherwise unwanted.
use checked_rs::prelude::*;
let mut val = HardClamp::<u8, Saturating, 0, 10>::new(5).unwrap();
assert_eq!(val.get(), 5);
let mut g = val.modify();
assert_eq!(g.is_changed(), false);
*g = 10;
assert_eq!(g.is_changed(), true);
g.commit().unwrap();
assert_eq!(val.get(), 10);
let mut g = val.modify();
*g = 15;
assert!(g.check().is_err());
g.discard();
assert_eq!(val.get(), 10);
§View
The View
struct is a wrapper around a value that encodes it’s validation logic into the wrapper. The Validator
trait is used to define the validation logic for a View
.
This wrapper is lightweight and can be used in place of the raw value via the Deref
and/or AsRef
traits.
use checked_rs::prelude::*;
#[derive(Clone, Copy)]
struct NotSeven;
impl Validator for NotSeven {
type Item = i32;
type Error = anyhow::Error;
fn validate(item: &Self::Item) -> Result<()> {
if *item == 7 {
Err(anyhow::anyhow!("Value must not be 7"))
} else {
Ok(())
}
}
}
let mut item = View::with_validator(0, NotSeven);
let mut g = item.modify();
*g = 7;
assert_eq!(*g, 7);
assert!(g.check().is_err());
*g = 10;
assert!(g.commit().is_ok());
// the guard is consumed by commit, so we can't check it again
// the `View`'s value should be updated
assert_eq!(&*item, &10);
§clamped
attribute macro
The clamped
attribute macro is used to create a specialized clamped type. The macro can only be used on enums where each variant represents a specific state within the clamped range.
This can be useful when there are multiple states that correspond to a certain set of rules and you want them to be easily distinguishable while still being able to use them in a single integer-like type.
use checked_rs::prelude::*;
#[clamped(u16, default = 600, behavior = Saturating, lower = 100, upper = 600)]
#[derive(Debug, Clone, Copy)]
enum ResponseCode {
#[eq(100)]
Continue,
#[eq(200)]
Success,
#[eq(300)]
Redirection,
#[eq(400)]
BadRequest,
#[eq(404)]
NotFound,
#[range(500..=599)]
ServerError,
#[other]
Unknown,
#[eq(600)]
Invalid,
}