3 个版本
使用旧的 Rust 2015
0.2.2 | 2017 年 9 月 1 日 |
---|---|
0.2.1 | 2017 年 9 月 1 日 |
0.2.0 | 2017 年 9 月 1 日 |
25KB
188 行
检测器
a•SEY•er, n.
- 分析物质的详细发现报告
- 一个不幸的人,他的工作是品尝皇家食物,以防食物中有毒
- 静态(编译时)验证库,用于 Rust
动机
通常,程序需要处理不受信任的输入。在这样做之前,确保正在处理的数据值确实适用于它们要完成的目标是很重要的。
例如,包括表示一天中的小时、月份中的日期、人的年龄、URL、电话号码等的数据值。无论底层数据类型(整数、字符串、自定义结构体等)如何,有些值是有效的,而有些则不是。
作为一个验证库,检测器允许您通过将自定义验证逻辑集中在单一位置——您的验证器——来构建高效、高度内聚的代码。您可以为需要验证的应用程序中的不同类型创建任意数量的验证器。
如何使用
快速入门
- 通过实现
ValidatorRef
和/或ValidatorMutRef
和/或Validator
来定义您的自定义验证器(例如,用于非空字符串),具体取决于您想要的移动/复制/可变性语义。以下是一个使用ValidatorRef
的示例
extern crate assayer;
use assayer::{ValidatorRef, ValidatorMutRef, Validator}; //Ok to import just the one(s) you want to use
use assayer::{MethodSyntaxValidatorRef, MethodSyntaxValidatorMutRef, MethodSyntaxValidator}; //ditto
use assayer::Error as AssayerError;
struct NonEmptyString; //A dummy custom type to validate
impl ValidatorRef<String> for NonEmptyString {
fn validate_ref(input: &String) -> std::result::Result<&String, AssayerError> {
Ok(input)
.and_then(|input| match !input.is_empty() {
true => Ok(input),
false => Err(AssayerError::ValueEmpty("<Validation failure message here>".to_string())),
})
// Chain as many additional .and_then() clauses as you need to validate your input.
// Each validation clause in the .and_then() chain must return:
// * Ok(input) if the value passes that validation step or
// * Err(String) if the value fails that validation step.
// A clause in a Result::and_then() chain which returns Err() will terminate further validation processing
// and the returned Err() will be returned to the caller.
//.and_then(|input| ...test #2...)
//...
//.and_then(|input| ...test #n...)
}
}
要调用验证器,请使用下面的函数或方法调用语法
//Function-call syntax example
let empty_string = String::new();
let function_call_syntax_result = String::validate_ref::<NonEmptyString>(&empty_string);
assert_eq!(function_call_syntax_result.err(),
Some(AssayerError::ValueEmpty("<Validation failure message here>".to_string())));
//Method-call syntax example
let non_empty_string = "Not an empty string".to_string();
let method_call_syntax_result = (&non_empty_string).validate_ref::<NonEmptyString>();
assert_eq!(method_call_syntax_result.ok(), Some(&non_empty_string));
使用说明:上面的只是一个快速示例,说明如何设置并调用库。在实际应用中,您可以让您的自定义类型构造函数在输入参数上调用验证器,这样一旦类型成功构造,就可以提供强大的保证,即其内容始终有效。
常见问题解答
检测器的一些设计目标是什么?
• 轻量级,无依赖
语言长期以来一直朝着引入其他“模块”并在其之上构建的方向发展(JavaScript 社区在这方面尤其突出)。Rust 的 Cargo 和 crate 系统为 Rust 带来了方便的依赖性,我们完全欢迎这一点。同时,我们是美丽、可维护、可预测、小而专注的代码的粉丝。当更少的代码可以完成更多的工作时,我们认为这种效率提升尤其优雅。因此,库最终只包含六个特质定义、三个泛型实现和没有依赖。
• 促进高度内聚、单一责任和不要重复原则
验证器允许您将所有检查因素化到前置条件中,所有这些都集中在同一个地方。当一个程序中的所有代码都专注于实现同一个目标时,您就会得到高度内聚,这已经被证明对代码库的可靠性和可维护性非常有益。
通过将验证代码集中在验证器中,验证器只需负责一件事:验证。单一职责原则有助于防止代码膨胀成“全能对象”,如果你曾经有幸与之合作,我们相信你一定会同意你不想很快再次回到它身边!
• 静态(编译时)验证器
作为一个静态验证器,你创建的验证链能够充分利用编译器的优化器。在运行时使用例如构建器模式的动态验证器无法实现这些优势,因为验证链在编译时并不存在作为链。
我们考虑了各种用例,但想不出静态验证器的缺点。如果你发现了动态验证器的有力论据,请告诉我们!
函数调用语法和方法调用语法的区别是什么?
从代码角度来看,没有区别。(对于我们针对Rust v1.18.0进行的测试,编译器生成的汇编指令100%相同。)
从个人角度来看,你可能更习惯于一种风格,或者在某些特定情况下使用一种特定的风格可能更方便。选择权在你。
我应该何时实现Validator
与ValidatorRef
与ValidatorMutRef
?
实现Validator
意味着你的验证器将消费调用者传递的值
impl Validator<String> for NonEmptyString {
fn validate(input: String) -> std::result::Result<String, AssayerError> {
Ok(input)
.and_then(|input| match !input.is_empty() {
true => Ok(input),
false => Err(AssayerError::ValueEmpty("<Validation failure message here>".to_string())),
})
}
}
fn main() {
let input = "Not an empty string".to_string();
let result = input.validate::<NonEmptyString>();
assert_eq!(result.ok(), Some(input)); //Oops! input has been moved!
}
如果这不可取,a) 调用者可以调用验证器时使用clone():input.clone().validate::<NonEmptyString>()
,或者b) 你可以选择实现ValidateRef
,调用者可以调用验证器如下:(&input).validate_ref::<NonEmptyString>()
。
实现ValidatorMutRef
的情况相对较少,并且可能违反单一职责原则。无论你是否认为它违反了,它可以用作在验证后代码使用之前‘修正’传入的值(例如,你可能想在使用之前填充或四舍五入有效的输入值)。以下是它的使用方法
impl ValidatorMutRef<String> for NonEmptyString {
fn validate_mut_ref(input: &mut String) -> std::result::Result<&mut String, AssayerError> {
Ok(input)
.and_then(|input| match !input.is_empty() {
true => { input.push_str("!"); Ok(input) }, //proof validate_mut_ref can mutate the input value
false => Err(AssayerError::ValueEmpty("<Validation failure message here>".to_string())),
})
}
}
fn main() {
let mut input = "Not an empty string".to_string();
let mut expected_result_inner = "Not an empty string!".to_string();
let expected_result = Some(&mut expected_result_inner);
let result = (&mut input).validate_mut_ref::<NonEmptyString>();
assert_eq!(result.ok(), expected_result);
}
最后,如果你想简单地使三种实现中的任何一种“正常工作”,根据你的需求实现ValidateRef,并按照以下方式实现ValidateMutRef和Validate的“空白实现”
impl ValidatorMutRef<String> for NonEmptyString {
fn validate_mut_ref(input: &mut String) -> Result<&mut String> {
Ok(input)
.and_then(|input| match (&*input).validate_ref::<NonEmptyString>() {
Ok(_) => Ok(input),
Err(err) => Err(err),
})
}
}
impl Validator<String> for NonEmptyString {
fn validate(input: String) -> Result<String> {
Ok(input)
.and_then(|input| match (&input).validate_ref::<NonEmptyString>() {
Ok(_) => Ok(input),
Err(err) => Err(err),
})
}
}