Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

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());
}

下降流程

  1. 用户创建初始 Arg
  2. Runtime::run 从起始 Trace 开始
  3. 按路径查找对应 Node, 调用其 process
  4. 若返回 Next(arg, next_trace), 则用新的 arg 和 trace 继续循环
  5. 若返回 Done(target), 则停止循环, 将 target 返回给用户
  6. 整个过程记录的路径可通过 runtime.path() 查看

Trace – 抽象路径

Tracearrival-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-tomlTOML 配置解析入口
arrival-cli命令行测试工具

这些 crate 的 API 可能在后续版本中调整.