tynavi 0.1.2

An immutable selector library for navigating, filtering, and backtracking through deeply nested Rust data structures.
Documentation

tynavi

tynavi(全称 Type Navigator)是一个零依赖的 Rust Selector 模式库,专注于两件事:

  • 用不可变、可链式的方式在数据结构中导航
  • 在选择子链路中保留父节点快照,支持回溯

它来自 onebot-apiselector feature(1.2.0 ~ 1.2.5),但被重构为一个更通用独立库

特性

  • 零外部依赖
  • Rust edition = "2024"
  • 要求工具链 >= 1.85
  • Selector<'a, Current, Parent> 同时追踪当前节点和父节点
  • 所有过滤 API 返回 Self,适合不可变链式调用
  • 支持 backtrack() / up() 回到父节点
  • 内置数字、字符串和智能指针类型扩展
  • 提供同步与异步过滤/提取接口

设计概念

核心类型:

Selector<'a, Current, Parent>
  • 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 tynavi::{
	selector::Selector,
	traits::AsSelector,
};

struct Profile {
	city: String,
	age: u32,
}

struct User {
	name: String,
	profile: Profile,
}

impl<'a> AsSelector<'a, User, ()> for User {
	fn as_selector(&'a self) -> Selector<'a, User, ()> {
		Selector::new(self)
	}
}

let user = User {
	name: "alice".to_owned(),
	profile: Profile {
		city: "Hangzhou".to_owned(),
		age: 20,
	},
};

let city = user
	.as_selector()
	.route_to(|u| Some(&u.profile))
	.route_to(|p| Some(&p.city))
	.starts_with("Hang")
	.extract(|city| city.to_owned());

assert_eq!(city, Some("Hangzhou".to_owned()));

let age_ok = user
	.as_selector()
	.route_to(|u| Some(&u.profile))
	.route_to(|p| Some(&p.age))
	.ge(&18)
	.is_matched();

assert!(age_ok);

父节点回溯

tynavi 的一个关键能力是父节点追踪

let profile_selector = user
	.as_selector()
	.route_to(|u| Some(&u.profile));

let city_selector = profile_selector
	.route_to(|p| Some(&p.city))
	.contains("zhou");

let parent = city_selector.backtrack();
assert!(parent.select().is_some());
  • 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():向上返回,并传播未匹配状态

已内置的类型扩展

数字类型

已支持:

  • i8 i16 i32 i64 i128 isize
  • u8 u16 u32 u64 u128 usize

每个数字类型都提供:

  • eq / not_eq
  • gt / not_gt
  • lt / not_lt
  • ge / not_ge
  • le / not_le
  • 对应的全部 cond_* 变体

字符串类型

已支持:

  • &str
  • String

提供:

  • starts_with
  • ends_with
  • contains
  • 对应的全部 cond_* 变体

智能指针类型

已支持:

  • Box<T>
  • Rc<T>
  • Arc<T>

提供:

  • as_ref():将 Selector<Box<T>, _>Selector<Rc<T>, _>Selector<Arc<T>, _> 路由到 Selector<T, _>

为自定义类型接入

接入一个自定义根类型时,通常只需要实现 AsSelector

use tynavi::{selector::Selector, traits::AsSelector};

struct Event {
	id: u64,
}

impl<'a> AsSelector<'a, Event, ()> for Event {
	fn as_selector(&'a self) -> Selector<'a, Event, ()> {
		Selector::new(self)
	}
}

如果你希望某个字段也拥有更自然的导航入口,可以继续围绕 Selector<'a, T, P> 为该类型扩展方法

构建与测试

cargo check

cargo build

cargo clippy

cargo fmt

cargo test

项目使用 .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 能力,适合:

  • 在嵌套结构中做只读导航与筛选
  • 需要保留父节点上下文的链式查询
  • 希望避免宏和外部依赖的轻量场景

如果后续继续扩展,比较自然的方向包括更多标准库类型支持,以及为业务模型补充更贴近领域的导航方法