checked-rs 0.2.0

A library for encoding validation semantics into the type system.
Documentation

checked-rs

checked-rs (referred to as checked) is a Rust library that includes generic types providing semantics for clamped unsigned integers and a general-purpose type that associates a data type with a validation type. This library was extracted from a larger side-project to make it generally available and showcase Rust skills and knowledge.

Installation

The checked library is compatible with rustc 1.79.0-nightly (a07f3eb43 2024-04-11) but does not use any opt-in language features. To install checked-rs, add the following to your Cargo.toml:

[dependencies]
checked-rs = "0.1.0"

Panicking vs Saturating

Behavior types are provide that configure the types to follow either panicking or saturating semantics when an underflow or overflow occurs. The clamped attribute macro defaults to panicking behavior if it is not otherwise specified.

Usage

Hard Clamp (Direct Interaction)

use checked_rs::HardClamp;
use checked_rs::behaviors::Saturating;

let mut clamp = HardClamp::<u8, Saturating, 0, 10>::new(5)?;

assert_eq!(clamp.get(), 5);

clamp += 5;

assert_eq!(clamp.get(), 10);

clamp -= 15;

assert_eq!(clamp.get(), 0);

clamp += 20;

assert_eq!(clamp.get(), 10);

Hard Clamp Guard

use checked_rs::{HardClamp, GuardState};
use checked_rs::behaviors::Saturating;

let mut clamp = HardClamp::<u8, Saturating, 0, 10>::new(5)?;

assert_eq!(clamp.get(), 5);

let mut g = clamp.modify();

assert_eq!(g.check(), GuardState::Unchanged);

*g = 10;

assert_eq!(g.check(), GuardState::Changed);

g.commit()?;

assert_eq!(clamp.get(), 10);

let mut g = clamp.modify();

assert_eq!(g.check(), GuardState::Unchanged);

*g = 15;

assert_eq!(g.check(), GuardState::Changed);

assert!(g.commit().is_err());

assert_eq!(clamp.get(), 10);

Soft Clamp

use checked_rs::SoftClamp;
use checked_rs::behaviors::Saturating;

let mut clamp = SoftClamp::<u8, Saturating, 0, 10>::new(5);
assert_eq!(*clamp, 5);
assert!(clamp.is_valid());

clamp += 5;
assert_eq!(*clamp, 10);
assert!(clamp.is_valid());

clamp -= 15;
assert_eq!(*clamp, 0);
assert!(clamp.is_valid());

*clamp = 30;
assert_eq!(*clamp, 30);
assert_eq!(clamp.is_valid(), false);

View

use checked_rs::{View, Validator, Result};

struct CheckedIntValidator;

impl Validator for CheckedIntValidator {
    type Item = i32;

    fn validate(item: &Self::Item) -> Result<()> {
        if *item < 0 {
            Err(anyhow::anyhow!("Value must be positive"))
        } else if *item % 2 == 0 && *item != 0 && *item <= 10 {
            Err(anyhow::anyhow!("Value must be odd, or zero, or greater than 10"))
        } else if *item == 7 {
            Err(anyhow::anyhow!("Value must not be 7"))
        } else {
            Ok(())
        }
    }
}

let mut item = View::with_validator(0, CheckedIntValidator);

assert_eq!(*item, 0);
assert!(item.is_valid());

*item = 1;
assert_eq!(*item, 1);
assert!(item.check().is_ok());

*item = -1;
assert_eq!(*item, -1);
assert!(item.check().is_err());

*item = 12;
assert_eq!(*item, 12);
assert!(item.check().is_ok());

*item = 7;
assert_eq!(*item, 7);

let item = match item.try_unwrap() {
    Ok(_) => panic!("Expected error"),
    Err(item) => {
        assert_eq!(*item, 7);
        item
    }
};

item.discard();

#[clamped(..)] Attribute Macro

use checked_rs::clamped;

#[clamped(u8; default = 1; behavior = Panicking)]
#[derive(Debug, Clone, Copy)]
pub enum Example {
    #[eq(0)]
    Nil,
    #[other]
    Valid,
    #[eq(u8::MAX)]
    Invalid,
}

let a: Example = Default::default();
let b: Example = 254.into();
let c = a + b;

assert!(a.is_valid());
assert!(b.is_valid());
assert!(c.is_invalid());

let d: Example = c - u8::MAX;

assert!(d.is_nil());

Features

  • HardClamp: Provides clamped unsigned integers with hard limits.
  • SoftClamp: Similar to HardClamp but allows invalid states.
  • View: Combines a data type with a validation type.
  • Guard: Allows temporary modifications with validation checks before committing changes.
  • Attribute Macro: #[clamped(..)] for creating enums with clamping behavior.

Contribution

Contributions are welcome! To contribute, please open a PR or a GitHub issue.

License

This project is dual-licensed under the Apache 2.0 and MIT licenses.

Contact and Support

For support or further questions, please open a GitHub issue.