# rust-utils
**Repository Path**: tx7do/rust-utils
## Basic Information
- **Project Name**: rust-utils
- **Description**: Rust toolbox ported from tx7do/go-utils: string case conversion, snowflake/order IDs, MySQL DDL parsing, Django-style query filters, priority event loop, captcha, JWT, AES & SM2/SM3/SM4 crypto, password hashing, geoip, translation, distributed locks — std-only core, feature-gated extras, 100% safe code.
- **Primary Language**: Rust
- **License**: Not specified
- **Default Branch**: main
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 0
- **Forks**: 0
- **Created**: 2026-09-15
- **Last Updated**: 2026-09-17
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
# rust-utils
[English](./README_en.md) | **中文** | [日本語](./README_ja.md)
---
Rust 工具箱:命名风格转换、切片/映射辅助、雪花 ID、订单号、查询条件解析、MySQL DDL 解析、优先级事件循环、数据回填、密码哈希、AES/HMAC、JWT、随机昵称生成、银行卡 BIN 查询、图形验证码、IP 归属地、机器翻译、分布式锁、国密等。
设计原则:
- **核心模块零依赖**(只依赖 `std`),重依赖的能力(时间、随机、加密、JWT 等)通过 feature 按需启用;
- 全 crate `#![forbid(unsafe_code)]`;
- 边界读取(geoip、ddl_parser 等)对越界与畸形输入一律返回错误或空结果,不做 panic。
## 使用
```toml
[dependencies]
rust-utils = "0.1"
# 按需启用 feature,例如:
# rust-utils = { version = "0.1", features = ["chrono", "rand", "password"] }
# 或全量:features = ["full"]
```
## 模块一览
| 模块 | feature | 说明 |
|---|---|---|
| [`byteutil`] | 默认 | 整数/字节互转、ASCII 大小写 |
| [`stringcase`] | 默认 | 驼峰/蛇形/烤肉串转换,识别缩写词与数字段 |
| [`stringutil`] | 默认 | 宽松数值/布尔解析、JSON 字段值重写 |
| [`sliceutil`] | 默认 | 查找族、交集/差集/并集、去重、分块 |
| [`maputil`] | 默认 | keys/values/merge/drop/filter |
| [`mathutil`] | 默认 | 统计量、手写正态分布(Gaussian)、Erfc/Ierfc |
| [`pagination`] | 默认 | 分页偏移量 |
| [`cryptocurrency`] | 默认 | 加密货币钱包地址格式校验 |
| [`query_parser`] | `json`(JSON 形式需要) | Django 风格 `field__op` 过滤/排序解析 |
| [`ddl_parser`] | 默认 | MySQL `CREATE TABLE` 解析(手写词法,零依赖) |
| [`eventloop`] | 默认 | 单线程优先级事件循环,支持帧驱动模式 |
| [`aggregator`] | 默认 | 关联数据回填(列表/树)+ 并行执行器 + 缓存式批量加载 |
| [`id`] | 默认(`uuid` 可选) | 雪花 ID、订单号、机器码(内置 SHA-256)、UUID v4/v7 |
| [`fsutil`] | 默认(`glob` 可选) | 文件/路径辅助 |
| [`dateutil`] | `chrono` | 日粒度取整、区间判断 |
| [`timeutil`] | `chrono` | 今天/昨天/本月/上月区间、时间差、格式转换 |
| [`random`] | `rand` | 随机工具:加权/别名表/骰子/抖动/正态/手机号等 |
| [`name_generator`] | `name-generator` | 随机昵称与中英日姓名(内嵌 2.5 MB 词库) |
| [`slug`] | `slug` | URL slug(unicode 转写) |
| [`password`] | `password` | PBKDF2/bcrypt/argon2/HMAC/SHA 哈希策略 |
| [`crypto`] | `crypto` | AES-CBC/AES-GCM/HMAC/SHA-2、PKCS#7 填充 |
| [`jwt`] | `jwt` | JWT 生成/解析/校验/刷新(HS256) |
| [`bank_card`] | `bank-card` | Luhn 校验 + BIN 查询(内嵌 2013 条记录) |
| [`captcha`] | `captcha` | 图形验证码:数字/字母/算术/中文四类文本驱动 + 滑块/点选/旋转,自绘渲染,输出 JSON 与 base64Captcha 前端组件兼容,存储层可插拔(`captcha-redis` 提供 Redis 落地) |
| [`fieldmask`] | `fieldmask` | 字段掩码:嵌套掩码树构建 + filter/prune/overwrite/validate/路径归一化,作用于 `serde_json::Value` |
| [`tls`] | `tls` | TLS 证书加载:从 PEM 文件/字节组装 rustls 服务端/客户端配置(单向/双向) |
| [`geoip`] | `geoip` | IP 归属地查询三后端:qqwry(GB18030、省/市切分)、ip2region xdb v2(向量索引+段索引二分、查询器池)、MaxMind mmdb 读取器;数据文件由调用方加载 |
| [`translator`] | `translator` | 翻译器四后端:百度(MD5 签名)/阿里(RPC 签名)/谷歌(v1 裸端点、v2/v3 REST)/火山(HMAC-SHA256 派生链);单次请求不自动重试,请求构造与签名可离线验证 |
| [`distlock`] | `distlock` | 分布式锁:Locker/Lock 抽象与获取选项(Redis 落地用 `distlock-redis`,兼容 bsm/redislock 线上协议;暂未提供 etcd 后端) |
| [`sm`] | `sm` | 国密 SM2(C1C3C2/ASN.1 加解密、签名验签)/SM3/SM4-CBC |
[`byteutil`]: src/byteutil.rs
[`stringcase`]: src/stringcase.rs
[`stringutil`]: src/stringutil.rs
[`sm`]: src/sm.rs
[`sliceutil`]: src/sliceutil.rs
[`maputil`]: src/maputil.rs
[`mathutil`]: src/mathutil.rs
[`pagination`]: src/pagination.rs
[`cryptocurrency`]: src/cryptocurrency.rs
[`query_parser`]: src/query_parser.rs
[`ddl_parser`]: src/ddl_parser.rs
[`eventloop`]: src/eventloop.rs
[`aggregator`]: src/aggregator.rs
[`id`]: src/id.rs
[`fsutil`]: src/fsutil.rs
[`dateutil`]: src/dateutil.rs
[`timeutil`]: src/timeutil.rs
[`random`]: src/random.rs
[`name_generator`]: src/name_generator.rs
[`slug`]: src/slug.rs
[`password`]: src/password.rs
[`crypto`]: src/crypto.rs
[`jwt`]: src/jwt.rs
[`bank_card`]: src/bank_card.rs
[`captcha`]: src/captcha.rs
[`fieldmask`]: src/fieldmask.rs
[`tls`]: src/tls.rs
[`geoip`]: src/geoip.rs
[`translator`]: src/translator.rs
[`distlock`]: src/distlock.rs
## 示例
### 命名风格转换(能识别缩写词和数字段)
```rust
use rust_utils::stringcase;
assert_eq!(stringcase::snake_case("HTTPStatusCode"), "http_status_code");
assert_eq!(stringcase::snake_case("Numbers123Test"), "numbers123_test");
assert_eq!(stringcase::upper_camel_case("parse_url.do_parse"), "ParseUrlDoParse");
assert_eq!(stringcase::kebab_case("Hello World!"), "hello-world");
```
### 雪花 ID 与订单号
```rust
use rust_utils::id;
let node = id::SnowflakeNode::new(1).unwrap();
let a = node.generate();
let b = node.generate();
assert!(b > a); // 趋势递增
let order = id::generate_order_id_with_random("ORD", None); // ORD + 14位时间戳 + 随机数
let order = id::generate_order_id_with_increase_index("ORD", None); // ORD + 14位时间戳 + 自增索引
```
### 查询条件解析
```rust
use rust_utils::query_parser;
let mut got = Vec::new();
query_parser::parse_filter_query_string(
"name__icontains:%E5%BC%A0,age__gte:18",
|field, op, value| got.push((field.to_string(), op.to_string(), value.to_string())),
).unwrap();
// got: [("name", "icontains", "张"), ("age", "gte", "18")]
```
### MySQL DDL 解析
```rust
use rust_utils::ddl_parser;
let table = ddl_parser::parse_create_table(
"CREATE TABLE `users` (\
`id` BIGINT AUTO_INCREMENT PRIMARY KEY, \
`userName` VARCHAR(100) NOT NULL COMMENT '用户名'\
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户表'",
).unwrap();
assert_eq!(table.name, "users");
assert!(table.columns[0].auto_increment);
assert_eq!(table.columns[1].comment, "用户名");
assert_eq!(table.engine, "innodb");
```
### 优先级事件循环
```rust
use rust_utils::eventloop::{new_request_event, EventLoop, EventProcessor, EventResult};
use std::sync::{Arc, Mutex};
use std::time::Duration;
struct Collector(Arc>>);
impl EventProcessor for Collector {
fn process(&mut self, event: Event) -> EventResult {
self.0.lock().unwrap().push(event.event_type.clone());
EventResult::ok(event.data)
}
}
let seen = Arc::new(Mutex::new(Vec::new()));
let loop_ = EventLoop::new(16, Collector(seen.clone()), false);
loop_.start();
// 请求事件带回执,便于等待处理完成
let (ev, rx) = new_request_event("ping", Some("hello".to_string()));
loop_.submit(ev).unwrap();
let reply = rx.recv_timeout(Duration::from_secs(5)).unwrap();
loop_.stop();
assert_eq!(reply.data.as_deref(), Some("hello"));
```
### 关联数据回填(避免 N+1)
```rust
use rust_utils::aggregator::populate;
use std::collections::HashMap;
struct Order { id: u32, user_name: Option }
let mut orders = vec![Order { id: 1, user_name: None }];
let users: HashMap = [(1, "张三".to_string())].into();
populate(&mut orders, &users, |o| o.id, |o, name| o.user_name = Some(name));
assert_eq!(orders[0].user_name.as_deref(), Some("张三"));
```
### 密码哈希
```rust
use rust_utils::password::{Argon2Crypto, Crypto};
let algo = Argon2Crypto;
let hash = algo.encrypt("s3cret!").unwrap(); // 标准 PHC 格式
assert!(algo.verify("s3cret!", &hash).unwrap());
```
### 验证码服务
七种驱动:数字/字母/算术/中文四类文本驱动,加滑块/点选/旋转三类
交互驱动;渲染为程序自绘,不依赖外部图片素材(字形资产说明见
`assets/captcha/README.md`)。文本驱动返回 PNG 数据 URI,交互驱动
返回 JSON,结构与 base64Captcha 前端组件一致。
```rust
use rust_utils::captcha::{Captcha, Config, DriverKind};
let cap = Captcha::with_config(Config::with_driver(DriverKind::Digit));
let (id, b64, answer) = cap.generate().unwrap();
// 文本驱动返回 PNG 数据 URI;滑块/点选/旋转驱动返回 JSON
// (内嵌 JPEG 主图 / PNG 缩略图)
assert!(b64.starts_with("data:image/png;base64,"));
assert!(cap.verify(&id, &answer).unwrap()); // 一次性校验
```
Redis 存储启用 `captcha-redis` feature 后,把 `Captcha::with_store(MemoryStore::new(), cfg)`
换成 `RedisStore::new("redis://127.0.0.1/")?` 即可,服务层 API 完全一致。
### 缓存式批量加载
```rust
use rust_utils::aggregator::Loader;
use std::sync::atomic::{AtomicUsize, Ordering};
let calls = std::sync::Arc::new(AtomicUsize::new(0));
let calls2 = calls.clone();
let loader = Loader::new(move |keys: &[u32]| {
calls2.fetch_add(1, Ordering::Relaxed);
keys.iter().map(|k| (*k, k * 10)).collect()
});
let got = loader.load_many(&[1, 2, 3]);
assert_eq!(got[&2].as_ref(), &20);
loader.load_many(&[2, 3]); // 全部命中缓存,不再取数
assert_eq!(calls.load(Ordering::Relaxed), 1);
```
## 开发
```bash
cargo test # 核心模块测试(零依赖)
cargo test --all-features # 全部模块测试(297 单测 + 28 文档测试,其中 1 例标记 ignore 不执行)
cargo clippy --all-features --all-targets
cargo fmt
```
## License
MIT