#validation #enums #clamp #checked #integer-value

checked-rs

将验证语义编码到类型系统的库

10个版本 (1个稳定版)

1.0.0 2024年7月21日
0.7.2 2024年6月17日
0.6.0 2024年6月15日
0.5.0 2024年6月15日
0.1.0 2024年6月11日

#313 in Rust模式

Download history 364/week @ 2024-06-09 507/week @ 2024-06-16 11/week @ 2024-06-23 23/week @ 2024-06-30 53/week @ 2024-07-07 127/week @ 2024-07-21 23/week @ 2024-07-28 1/week @ 2024-08-04

每月151次下载

MIT/Apache

53KB
1K SLoC

checked-rs

checked-rs (称为 checked) 是一个Rust库,它包含一个泛型类型,用于将任意验证逻辑编码到类型系统,以及一个proc-macro用于生成特定的整数类型。

此库是从一个更大的侧项目中提取出来的,以便使其普遍可用,并展示Rust技能和知识。

安装

checked 库与 rustc 1.79.0-nightly (a07f3eb43 2024-04-11) 兼容,但未使用任何可选语言功能。要安装 checked-rs,请将以下内容添加到您的 Cargo.toml

[dependencies]
checked-rs = "1.0"

概述

此库的主要组件是属性宏 clamped 和结构体 View(以及 Validator 特性)。此外,还有一些特性和类型,例如 BehaviorClampGuard,它们可以配置如何处理溢出,或提供与约束类型的替代交互方式。

clamped 函数宏

此proc-macro简化了针对特定约束和使用情况创建特定整数类型的创建,增强了Rust项目中的类型安全和代码清晰度。生成的类型可以分为两大类

1. 包装类型

此类创建一个包装任何大小的整数类型的类型,保留相同的内存布局。包装类型可以可选地编码整数值的上限和/或下限。它进一步分为两个子类别

硬包装

  • 不变量:值保证永远不会超过指定的界限。
  • 用途:在类型级别强制执行严格的边界强制时理想。

软包装

  • 灵活性:值可以是底层类型的任何有效整数。
  • 方法:实现方法以检查值是否位于指定的界限内。
  • 用途:适用于需要边界检查但通过运行时方法而不是类型级别强制执行的案例。

2. 枚举类型

此类生成任何大小的整数的枚举类型,其变体描述

  • 精确值。
  • 值范围。
  • 嵌套枚举类型。

顶层枚举类型也支持与“Hard”包装类型相同的行为,如果变体表示有上界和/或下界。此外,此类别递归生成类型以支持由变体描述的范围和嵌套枚举。

  • 精确值:以单独变体表示的特定整数值。
  • 范围:包含一系列值的变体。
  • 嵌套枚举:自身也是枚举的变体,允许进行复杂和分层类型定义。

使用和配置

由proc-macro生成的类型类别由输入中指定的语言项类型确定,无论是struct还是enum

指定目标整型类型

目标整型类型在语言项上方提供。将属性中的括号内容解析为各种配置选项,允许用户根据特定需求调整生成的类型。以下是一些使用属性的示例

在这些文档的其余部分中,int将用于指代用于夹逼值的整型。

生成包装器

指定范围

类型应恰好有一个未命名的字段,但提供类型而不是声明类型,而是提供类型覆盖的范围。所有范围形式都是允许的。

#[usize]
struct Exclusive(10..100);

#[usize]
struct Inclusive(10..=100);

#[usize]
struct OpenEnd(10..);

#[usize]
struct OpenStart(..100);

基本属性

指定基本包装类型而不进行额外配置

#[usize]

指定软或硬行为

对于包装类型,您可以指定行为应该是Soft还是Hard

  • 软行为
#[usize as Soft]

这允许值为基本类型的任何有效整数值,并提供方法来检查它是否在范围内。

  • 硬行为
#[usize as Hard]

这强制值不能超出指定的范围。

  • 其他派生宏 可以将其他特质应用于生成的类型,以包括其他特质。例如,除了始终派生的 Clone 和 Copy 特质之外,还派生 Debug 特质
#[usize as Soft; derive(Debug)]

默认值

proc-macro 允许为生成的类型指定默认值。此默认值可以是推断的,也可以是手动指定的。

推断默认值

默认值可以是推断为范围的最低值。

手动指定默认值

或者,您可以使用属性手动指定默认值。以下是如何指定默认值的示例

#[usize; default = 10]

自动生成的特质

proc-macro自动为生成的类型生成各种特质定义,确保它们能够无缝地与Rust的类型系统和标准库集成。以下特质已实现

  • PartialEq 与自身和基本类型。
  • Eq
  • PartialOrd 与自身和基本类型。
  • Ord
  • 软和硬版本都实现了 DerefAsRef<int>
  • 只有软版本实现了 DerefMutAsMut<int>
  • 适用于其他内置整型的任何适用转换特质。
  • 二进制运算:AddSubMulDivRemBitAndBitOrBitXor以及这些特质的任何适用的___Assign版本。

特质使用示例

以下是如何使用自动实现特质生成类型的示例

// Define a soft wrapper type over usize with a default value of 10
#[usize as Soft; default = 10; derive(Debug)]
struct MySoftBoundedIntWithDefault(0..100);

// Define a hard wrapper type over u32 with a default value of 0
#[u32 as Hard; default = 0]
struct MyHardBoundedIntWithDefault(0..100);

# fn main() {
let a = MySoftBoundedIntWithDefault::default();
let b = MySoftBoundedIntWithDefault::from(15);
let c = MyHardBoundedIntWithDefault::default();

// PartialEq and PartialOrd
assert!(a != b);
assert!(a < b);

// Deref and AsRef
assert_eq!(*a, 10);
assert_eq!(a.as_ref(), &10);

// DerefMut and AsMut (soft only)
let mut d = b;
*d = 20;
assert_eq!(*d, 20);
assert_eq!(d.as_mut(), &mut 20);

// Binary operations
let sum = a + d;
let product = c * 5;
assert_eq!(*sum, 30);
assert_eq!(*product, 0);
# }

生成枚举

文档正在更新中

查看

View结构体是围绕一个值的一个包装器,它将验证逻辑编码到包装器中。使用Validator特质来定义View的验证逻辑。此包装器轻量级,可以通过Deref和/或AsRef特质替代原始值。

# 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);

依赖项

~6.5MB
~121K SLoC