1 个不稳定版本
0.1.0 | 2024年4月22日 |
---|
#353 在 过程宏 中
15KB
175 行
Rust的回调函数
callback_fn 是一个库,可以在目标函数之前、之后和周围添加函数。
特性
- 自定义函数:用户可以指定在目标函数之前、之后和周围执行的函数。
- 无缝集成:与现有代码库无缝集成。
- 错误处理:处理回调函数中发生的错误
使用
- 回调函数:在目标函数前后添加函数。
- 适用于日志记录、身份验证和其他横切关注点等任务。
- 契约设计:向目标函数添加前置条件和后置条件。
- 可以应用特定条件,例如仅在测试时、使用功能标志。
安装
将 callback_fn 添加到您的 Cargo.toml
。
[dependencies]
callback_fn = "0.1.0"
示例
对于回调
用户创建后,将创建用户缓存。
use callback_fn::after_callback;
#[allow(dead_code)]
#[derive(Clone, Debug)]
struct User {
name: String,
}
struct UserRepository {}
impl UserRepository {
async fn save(&self, _user: User) -> Result<(), String> {
tokio::time::sleep(Duration::from_micros(1)).await;
Ok(())
}
}
struct UserCache {}
impl UserCache {
async fn save(&self, _user: User) -> Result<(), String> {
tokio::time::sleep(Duration::from_micros(1)).await;
Ok(())
}
}
struct UserUseCase {
user_repository: UserRepository,
user_cache: UserCache,
}
impl UserUseCase {
#[after_callback(let _ = self.create_user_cache(ret.clone()?).await?)]
async fn create_user(&self, name: String) -> Result<User, String> {
let user = User { name };
self.user_repository.save(user.clone()).await?;
Ok(user)
}
async fn create_user_cache(&self, user: User) -> Result<User, String> {
self.user_cache.save(user.clone()).await?;
Ok(user)
}
}
对于日志记录
在目标函数周围添加日志。
use callback_fn::around_callback;
#[around_callback(my_logger())]
fn hello(str: &str) {
println!("Hello {}", str);
}
fn my_logger() {
println!("{}", chrono::Local::now());
}
// hello will print:
//
// 2024-04-01T00:00:000.000000+09:00
// Hello world
// 2024-04-01T00:00:000.000100+09:00
#[test]
fn test_hello() {
hello("world");
}
对于身份验证
在 UseCase 函数之前添加身份验证。
use callback_fn::before_callback;
use strum_macros::Display;
#[before_callback(has_permission(current_user, Permission::ReadPost).map_err(UseCaseError::from)?)]
fn get_post_by_id(current_user: &User, id: usize) -> Result<Post, UseCaseError> {
Ok(Post {
id,
title: "Dummy Title".to_string(),
body: "Dummy Body".to_string(),
})
}
#[before_callback(has_permission(current_user, Permission::CreatePost).map_err(UseCaseError::from)?)]
fn create_post(current_user: &User, title: String, body: String) -> Result<Post, UseCaseError> {
Ok(Post { id: 1, title, body })
}
#[derive(Debug)]
struct User {
permissions: Vec<Permission>,
}
#[derive(Debug, Display, PartialEq)]
pub enum Permission {
ReadPost,
CreatePost,
}
fn has_permission(user: &User, permission: Permission) -> Result<(), PermissionError> {
if user.permissions.contains(&permission) {
Ok(())
} else {
Err(PermissionError::PermissionDenied(permission))
}
}
#[derive(Debug, PartialEq)]
struct Post {
id: usize,
title: String,
body: String,
}
#[derive(thiserror::Error, Debug)]
pub enum PermissionError {
#[error("User don't have {0} permission.")]
PermissionDenied(Permission),
}
#[derive(thiserror::Error, Debug)]
pub enum UseCaseError {
#[error("PermissionError: {0}")]
PermissionError(#[from] PermissionError),
}
对于契约设计
在添加到购物车或清理后,确保 total_price 是正确的。
use callback_fn::around_callback;
struct Cart {
total_price: usize,
items: Vec<Item>,
}
struct Item {
price: usize,
}
impl Cart {
fn new() -> Self {
Self {
total_price: 0,
items: vec![],
}
}
// Ensure total_price is correct around add_item.
// Error handling is available in runtime when conditions are not ensure.
#[around_callback(self.ensure_total_price()?)]
fn add_item(&mut self, item: Item) -> Result<(), String> {
self.items.push(item);
self.update_total_price();
Ok(())
}
fn update_total_price(&mut self) {
self.total_price = self.items.iter().map(|item| item.price).sum()
}
fn ensure_total_price(&self) -> Result<(), String> {
if self.total_price == self.items.iter().map(|item| item.price).sum() {
Ok(())
} else {
Err("Total price is not correct".to_string())
}
}
}
仅在特定功能中使用
如果您只想在特定功能中使用 callback_fn,可以使用 cfg_attr
。
use callback_fn::after_callback;
#[cfg_attr(test, after_callback(bar()))]
fn foo() {
println!("foo");
}
fn bar() {
println!("bar");
}
如果您运行 cargo run --features test
,foo 函数将如下所示。
fn foo() {
#[allow(unused_mut)]
let mut ret = {
{
::std::io::_print(format_args!("foo\n"));
};
};
bar();
ret
}
fn bar() {
{
::std::io::_print(format_args!("bar\n"));
};
}
如果您运行 cargo run
,foo 函数将如下所示。
fn foo() {
{
::std::io::_print(format_args!("foo\n"));
};
}
fn bar() {
{
::std::io::_print(format_args!("bar\n"));
};
}
依赖项
~225–660KB
~16K SLoC