tynavi
tynavi(全称 Type Navigator)是一个零依赖的 Rust Selector 模式库,专注于两件事:
- 用不可变、可链式的方式在数据结构中导航
- 在选择子链路中保留父节点快照,支持回溯
它来自 onebot-api 的 selector feature(1.2.0 ~ 1.2.5),但被重构为一个更通用独立库
特性
- 零外部依赖
- Rust
edition = "2024" - 要求工具链
>= 1.85 Selector<'a, Current, Parent>同时追踪当前节点和父节点- 所有过滤 API 返回
Self,适合不可变链式调用 - 支持
backtrack()/up()回到父节点 - 内置数字、字符串和智能指针类型扩展
- 提供同步与异步过滤/提取接口
设计概念
核心类型:
Current:当前游标指向的类型Parent:父节点快照类型cursor: Option<&'a Current>:当前是否匹配
和常见的可变 Selector 设计不同,tynavi 的方法不会原地修改自身,而是返回新的快照,这使得链式调用更稳定,也让“当前节点”和“父节点”可以安全一起传递
这个库解决什么问题
tynavi 最初来自 onebot-api 的事件处理场景(1.2.0 ~ 1.2.5),它主要解决的不是“完全替代模式匹配”,而是替代那些在深层嵌套事件结构中,为了取字段、筛选条件和保留上下文而产生的大量样板 match
它尤其适合下面几类问题:
- 深层嵌套枚举或结构体访问过于冗长
- 事件处理中“先取值、再判断、再继续下钻”的流程分散在多层
match + if中 - 中间任意一步不匹配时,需要反复手写失败分支
- 深入某个子字段检查后,还需要回到父事件继续处理
- 相似的事件筛选逻辑难以沉淀成可复用的小段能力
换句话说,tynavi 更擅长表达:
- 我要进入哪个字段
- 我要对当前值施加什么条件
- 匹配成功后我要提取什么
- 必要时我要回到哪一层上下文
在这种场景下,链式 Selector 往往比直接展开多层模式匹配更接近业务意图
不打算解决什么问题
tynavi 也有明确边界,它并不适合替代所有 match:
- 当逻辑本身是复杂分支控制流时,
match通常更直接 - 当你需要穷尽枚举分支并显式处理每一种情况时,模式匹配更清晰
- 当导航路径本身不稳定、需要大量自定义提取器时,链式调用的可读性未必比手写控制流更好
因此更准确的定位是:
tynavi 用来处理“深层数据导航 + 条件筛选 + 结果提取 + 父上下文回退”这类问题,而不是用来取代 Rust 里所有的模式匹配
快速开始
use ;
let user = User ;
let city = user
.as_selector
.route_to
.route_to
.starts_with
.extract;
assert_eq!;
let age_ok = user
.as_selector
.route_to
.route_to
.ge
.is_matched;
assert!;
父节点回溯
tynavi 的一个关键能力是父节点追踪
let profile_selector = user
.as_selector
.route_to;
let city_selector = profile_selector
.route_to
.contains;
let parent = city_selector.backtrack;
assert!;
backtrack():直接返回父节点快照up():回到父节点,并在当前未匹配时把未匹配状态向上传递
这让“先深入筛选,再回到上层继续处理”成为可能
核心 API
路由与转换
route_to(extractor):进入子字段,父节点变为当前快照replace(v):替换当前游标map(f):将当前引用映射到另一引用
过滤
filter(f)cond_filter(condition, f)filter_async(f)cond_filter_async(condition, f)
约定:
- 当
condition == false时,所有cond_*方法都直接返回snapshot() - 过滤失败后,Selector 会进入未匹配状态
提取与检查
select():返回Option<&Current>extract(f):提取值,返回Option<R>extract_async(f):异步提取is_matched():检查当前是否匹配require_matched():返回SelectorResult<Self>
回溯
parent():获取父节点快照backtrack():返回父节点up():向上返回,并传播未匹配状态
已内置的类型扩展
数字类型
已支持:
i8i16i32i64i128isizeu8u16u32u64u128usize
每个数字类型都提供:
eq/not_eqgt/not_gtlt/not_ltge/not_gele/not_le- 对应的全部
cond_*变体
字符串类型
已支持:
&strString
提供:
starts_withends_withcontains- 对应的全部
cond_*变体
智能指针类型
已支持:
Box<T>Rc<T>Arc<T>
提供:
as_ref():将Selector<Box<T>, _>、Selector<Rc<T>, _>、Selector<Arc<T>, _>路由到Selector<T, _>
为自定义类型接入
接入一个自定义根类型时,通常只需要实现 AsSelector:
use ;
如果你希望某个字段也拥有更自然的导航入口,可以继续围绕 Selector<'a, T, P> 为该类型扩展方法
构建与测试
项目使用 .rustfmt.toml,采用硬制表符格式
与 onebot-api Selector 的区别
| 特性 | onebot-api(1.2.0 ~ 1.2.5) | tynavi |
|---|---|---|
| 类型签名 | Selector<'a, T> |
Selector<'a, Current, Parent> |
| 可变性 | &mut self 风格 |
返回 Self 的不可变快照 |
| 父节点追踪 | 无 | 有 |
| 使用范围 | onebot-api 事件模型 | 任意 Rust 类型 |
当前状态
这个库目前已经具备可用的基础 Selector 能力,适合:
- 在嵌套结构中做只读导航与筛选
- 需要保留父节点上下文的链式查询
- 希望避免宏和外部依赖的轻量场景
如果后续继续扩展,比较自然的方向包括更多标准库类型支持,以及为业务模型补充更贴近领域的导航方法