Introduction
Arrival 是一个抽象层通信框架. 请求沿节点路径逐层下降, 直到找到答案.
核心理念
整个框架围绕四个核心概念构建:
- Arg: 在节点间传递的请求
- Target: 最终返回的结果
- Node: 处理请求的单个层, 决定是继续下降还是返回结果
- Runtime: 管理整个下降过程的执行引擎
节点之间通过 Trace (抽象路径) 进行路由, 详细说明见后续章节.
使用场景
- 配置查找 – 按路径逐层匹配
- 命令路由 – 根据输入转发到不同处理器
- 协议协商 – 多层 fallback 机制
- 文件系统抽象 – 路径到资源的映射
- 多阶段流水线 – 数据在节点间流动加工
项目状态
核心 API 已稳定, 正在积极开发更多内置节点类型.
Core
本章介绍 arrival-core 的核心类型和用法.
依赖
[dependencies]
arrival-core = "0.2"
核心 Trait
Arg – 请求
在节点间传递的参数.
#![allow(unused)]
fn main() {
pub trait Arg {
fn to_string(&self) -> String;
}
}
Target – 结果
下降终点返回的响应.
#![allow(unused)]
fn main() {
pub trait Target {
fn to_string(&self) -> String;
}
}
Node – 节点
框架的核心抽象. 每个节点有一个路径标识, 接收 Arg 并返回 NodeResult.
#![allow(unused)]
fn main() {
pub trait Node {
fn path(&self) -> Trace;
fn process(&self, arg: &dyn Arg) -> NodeResult;
}
}
NodeResult – 节点的决策
#![allow(unused)]
fn main() {
pub enum NodeResult {
Next(Box<dyn Arg>, Trace), // 继续下降到下一个节点
Done(Box<dyn Target>), // 停止, 返回结果
}
}
Runtime – 执行引擎
Runtime 管理所有注册的节点, 驱动下降流程.
#![allow(unused)]
fn main() {
pub struct Runtime {
nodes: Vec<Box<dyn Node>>,
path: Trace,
}
}
关键方法:
| 方法 | 说明 |
|---|---|
new() | 创建空 Runtime |
add_node(node) | 注册一个节点 |
get(path) | 按路径查找节点 |
run(arg, start) | 从起始路径开始执行下降 |
path() | 查看当前下降链路的完整 Trace |
reset() | 重置路径记录 |
完整示例
下面是一个包含根节点和子节点的完整例子:
use arrival_core::{Arg, Target, Node, NodeResult, Runtime, Trace};
// -- Arg --
struct MyArg {
raw: String,
}
impl Arg for MyArg {
fn to_string(&self) -> String {
self.raw.clone()
}
}
// -- Target --
struct MyTarget {
value: String,
}
impl Target for MyTarget {
fn to_string(&self) -> String {
self.value.clone()
}
}
// -- Node: root --
struct RootNode;
impl Node for RootNode {
fn path(&self) -> Trace {
Trace::from_str("root")
}
fn process(&self, arg: &dyn Arg) -> NodeResult {
if arg.to_string().contains("hello") {
NodeResult::Done(Box::new(MyTarget {
value: "hello from root".to_string(),
}))
} else {
NodeResult::Next(
Box::new(MyArg {
raw: format!("forwarded: {}", arg.to_string()),
}),
Trace::from_str("root::child"),
)
}
}
}
// -- Node: child --
struct ChildNode;
impl Node for ChildNode {
fn path(&self) -> Trace {
Trace::from_str("root::child")
}
fn process(&self, arg: &dyn Arg) -> NodeResult {
NodeResult::Done(Box::new(MyTarget {
value: format!("child processed: {}", arg.to_string()),
}))
}
}
fn main() {
let mut runtime = Runtime::new();
runtime.add_node(Box::new(RootNode));
runtime.add_node(Box::new(ChildNode));
let arg = Box::new(MyArg {
raw: "hello".to_string(),
});
let result = runtime.run(arg, Trace::from_str("root"));
println!("{}", result.unwrap().to_string());
}
下降流程
- 用户创建初始 Arg
Runtime::run从起始 Trace 开始- 按路径查找对应 Node, 调用其
process - 若返回
Next(arg, next_trace), 则用新的 arg 和 trace 继续循环 - 若返回
Done(target), 则停止循环, 将 target 返回给用户 - 整个过程记录的路径可通过
runtime.path()查看
Trace – 抽象路径
Trace 是 arrival-trace crate 提供的抽象路径类型, 用于在 Node 之间路由请求.
各段的类型由使用者决定, 不局限于文件系统路径.
基本结构
#![allow(unused)]
fn main() {
pub struct Trace {
segments: Vec<Box<dyn Segment>>,
left_open: bool,
right_open: bool,
}
}
segments: 路径的各个分段left_open: 左侧是否开放 (允许在前方继续拼接)right_open: 右侧是否开放 (允许在后方继续拼接)
Display 格式
各段以 :: 连接. 开放端额外附加 ::.
[a, b] 左右闭合 => a::b
[a, b] 左侧开放 => ::a::b
[a, b] 右侧开放 => a::b::
[a, b] 两端开放 => ::a::b::
构造
从字符串解析
#![allow(unused)]
fn main() {
use arrival_trace::Trace;
let t = Trace::from_str("root::child::leaf");
let open = Trace::from_str("::root::child::");
}
前缀 :: 表示 left_open, 后缀 :: 表示 right_open.
逐步构建
#![allow(unused)]
fn main() {
let mut t = Trace::new();
t.push_str("root");
t.push_str("child");
assert_eq!(t.to_string(), "root::child");
}
自定义 Segment
#![allow(unused)]
fn main() {
use arrival_trace::segment::Segment;
#[derive(Debug)]
struct IdSegment(u32);
impl ToString for IdSegment {
fn to_string(&self) -> String { self.0.to_string() }
}
impl Segment for IdSegment {
fn is_empty(&self) -> bool { false }
}
let mut t = Trace::new();
t.push(IdSegment(42));
}
API 参考
| 方法 | 说明 |
|---|---|
new() | 创建空路径 |
from_str(s) | 从 :: 分隔的字符串解析 |
push(segment) | 追加任意 Segment |
push_str(s) | 以字符串追加一段 |
segments_str() | 返回各段的 Vec<String> |
is_empty() | 路径是否为空 |
to_string() (Display) | 输出 :: 分隔的字符串 |
Segment trait
#![allow(unused)]
fn main() {
pub trait Segment: ToString + std::fmt::Debug {
fn is_empty(&self) -> bool;
}
}
默认提供 StringSegment, 封装 String. 启用 segment-string feature (默认) 即可使用.
在 Runtime 中的角色
每次下降时 Runtime 将当前路径追加入内部的 Trace, 形成完整的链路记录:
#![allow(unused)]
fn main() {
let mut runtime = Runtime::new();
runtime.run(arg, Trace::from_str("root"));
// 查看完整下降路径
println!("{:?}", runtime.path().segments_str());
}
In Developing
以下 crate 正在开发中, 提供内置节点实现和周边工具.
| Crate | 说明 |
|---|---|
arrival-string | 基于字符串的 Node, 返回固定响应 |
arrival-cli-return | 调用命令行并返回结果的 Node |
arrival-serde | 序列化支持, 可从 TOML/JSON 定义节点 |
arrival-toml | TOML 配置解析入口 |
arrival-cli | 命令行测试工具 |
这些 crate 的 API 可能在后续版本中调整.