moonthrift

    Apache Thrift IDL tooling and binary protocol support for MoonBit

    thrift
    serialization
    idl
    code-generation
    rpc
    Download zip
    Author
    Version
    0.3.0
    License
    Apache-2.0
    Last updated
    3 days ago
    Downloads
    47

    Dependencies

    #MoonThrift

    CI Mooncakes License

    MoonThrift 是一个用 MoonBit 编写的 Apache Thrift 基础工具库。它把 Thrift IDL 解析、语义检查、协议编解码、代码生成和接口兼容性检查放在同一套可复用 API 中,可用于构建 RPC 运行时、协议调试工具、Schema 仓库和数据迁移流程。

    项目当前聚焦与网络框架无关的核心能力,不绑定某一种 HTTP、Socket 或异步 运行时,因此库和测试可在 MoonBit 的 wasm、wasm-gc、JavaScript、native 四个 稳定后端运行;文件和网络 I/O 只放在 native 限定的适配层或示例中。

    #已实现功能

    • Thrift IDL lexer、带行列位置的 token 与错误信息;
    • 解析 include、namespace、const、typedef、enum、struct、 union、exception、service、throws、容器类型和 annotations;
    • 检查重复定义、未解析类型、字段 ID/名称冲突、union required 字段、 oneway 约束和 service 继承;
    • 动态 Thrift 值模型,无需先生成代码即可检查协议数据;
    • 标准 Binary Protocol 和 Compact Protocol 的值、容器、结构体编解码;
    • Binary/Compact RPC message envelope 编解码;
    • 深度、容器元素数、二进制长度限制,畸形输入以明确错误返回;
    • 从 Thrift Schema 生成 MoonBit typedef、enum、struct、union、exception 以及 service 的参数/结果模型;
    • 为生成模型提供类型安全的 to_thrift_value / from_thrift_value 与 Binary/Compact 便捷方法,支持嵌套容器、默认值、未知字段和 required 校验;
    • 与 Apache Thrift Python 0.24.0 进行 Binary/Compact 双向字节级互操作验证, 覆盖整数边界、Unicode、空/嵌套容器、异常和未知字段;
    • 将 /// 和 /** ... */ IDL 文档保留为生成 MoonBit API 的文档注释;
    • 按稳定字段 ID、枚举数值、方法名比较两个版本,区分 compatible、warning、 breaking 变更;
    • check、inspect、generate、diff、compat 五个 CLI 工作流;
    • backward、forward、full 兼容性策略,以及稳定 JSON、Markdown、GitHub annotations 报告;支持规则抑制、目录和 Git 提交基线。
    • 可移植的单次 RPC 请求/响应处理和内存字节传输,支持 Binary/Compact; 类型化客户端/处理器、无响应 ONEWAY、应用异常和有长度上限的分帧传输;
    • 调用方提供源码加载器的多文件 workspace,支持相对 include、循环检测、 限定类型与跨文件 service 继承检查;

    #快速开始

    安装依赖:

    moon add Xpeng/moonthrift

    解析和检查 IDL:

    let idl =
    #|struct User {
    #| 1: required i64 id,
    #| 2: optional string name
    #|}
    #|

    let (schema, diagnostics) = @moonthrift.compile_idl(idl)
    assert_eq(schema.definitions.length(), 1)
    assert_eq(diagnostics, [])

    动态值可以在不依赖生成代码的情况下经过两种协议往返:

    let value : @protocol.Value = StructValue([
    { id: 1, value: I64Value(7L) },
    { id: 2, value: BinaryValue(b"MoonBit") },
    ])

    let binary = @protocol.encode_binary(value)
    assert_eq(@protocol.decode_binary(binary, Struct), value)

    let compact = @protocol.encode_compact(value)
    assert_eq(@protocol.decode_compact(compact, Struct), value)

    对应的 moon.pkg:

    import {
    "Xpeng/moonthrift",
    "Xpeng/moonthrift/protocol",
    }

    #CLI 示例

    仓库提供了可直接运行的 tutorial.thrift:

    # 内置解析与 Compact Protocol 往返示例 moon run cmd/main --target native # 语法与语义检查 moon run cmd/main --target native -- check examples/tutorial.thrift # 查看 Schema 轮廓 moon run cmd/main --target native -- inspect examples/tutorial.thrift # 递归检查和查看多文件 Schema moon run cmd/main --target native -- check examples/multifile/api.thrift moon run cmd/main --target native -- inspect examples/multifile/api.thrift # 把多文件 Schema 生成为一个可直接编译的 MoonBit 模型文件 moon run cmd/main --target native -- generate \ examples/multifile/api.thrift generated.mbt # 生成 MoonBit 数据模型 moon run cmd/main --target native -- generate examples/tutorial.thrift generated.mbt # 比较两个 Schema 版本;发现 breaking change 时退出码为 3 moon run cmd/main --target native -- diff \ examples/tutorial.thrift examples/tutorial-v2.thrift # 比较两个目录;breaking 变更使检查失败 moon run cmd/main --target native -- compat --policy backward --format json \ examples/compatibility/before examples/compatibility/after # 将当前 Schema 目录与 Git 基线比较,适合 PR CI python tools/compat_git.py --base-ref main --schema-dir examples \ --policy backward --format github # 使用生成的服务模型进行一次内存 RPC 往返 moon run examples/rpc_demo --target native

    examples/generated/model.mbt 是由示例 IDL 生成并 纳入四后端编译与往返测试的结果,防止生成器只“输出文本”却无法被 MoonBit 使用。生成代码所在包需要导入协议包;包含普通 request/reply 服务的 生成文件还需要导入 rpc 包:

    import {
    "Xpeng/moonthrift/protocol",
    "Xpeng/moonthrift/rpc",
    }

    生成后的记录类型可以直接往返,无需手工构造动态 Value:

    let encoded = user.encode_compact()
    let decoded = User::decode_compact(encoded)
    assert_eq(decoded, user)

    #包结构

    包用途
    Xpeng/moonthriftIDL AST、解析、语义检查、兼容性比较
    Xpeng/moonthrift/protocol动态值、Binary/Compact codec、RPC message
    Xpeng/moonthrift/codegenMoonBit 源码生成器
    Xpeng/moonthrift/rpc可移植的消息处理与内存 RPC 传输
    cmd/mainnative 文件与命令行适配层

    详细数据流、功能边界和维护方向见 docs/architecture.md,协议实现与安全限制见 docs/protocols.md,复现测试的方法见 docs/verification.md,跨语言夹具与验证矩阵见 docs/interoperability.md。 Schema 版本策略、规则抑制和可复制的 CI 工作流见 docs/compatibility-ci.md。 RPC 的协议/传输边界和当前未实现范围见 docs/rpc-runtime.md。

    #当前边界

    0.3.0 已提供可移植的单次 RPC 处理、内存传输、类型化客户端与 处理器、应用异常、无响应的 ONEWAY 调用和可增量解码的分帧传输。 native TCP 回环示例展示如何把这些能力接到真实连接上,但它仅处理每个连接的一次 请求,不是可复用的异步网络框架。继承服务运行时、长期运行的并发服务端、TLS、 连接池、多路复用和其他语言生成器仍在范围之外。

    #质量与开源说明

    CI 会检查格式、公开接口漂移、四后端 check/build/test、CLI 示例、生成结果、 package artifact 和工作区洁净度。项目为原创 MoonBit 实现,兼容行为参考 Apache Thrift 公开规范,没有复制上游源码;来源和许可证说明见 THIRD_PARTY.md。

    参与方式见 CONTRIBUTING.md,安全问题请按 SECURITY.md 私下报告。本项目采用 Apache-2.0 许可证。

    native TCP 回环教程可运行 moon run examples/tcp_demo --target native。它使用随机本地端口演示 Binary/Compact 分帧 RPC,并位于独立的 native 包,不影响核心库的四后端兼容性。

    SourceLoader

    type SourceLoader = (String) -> String?

    Provides source text for a normalized logical Thrift path.

    Returning None reports a source-not-found diagnostic. The loader remains caller-owned so this package stays portable across every stable backend.

    IdlError

    pub(all) suberror IdlError {
    UnexpectedCharacter(Char, Span)
    UnterminatedString(Span)
    UnterminatedComment(Span)
    InvalidEscape(Char, Span)
    InvalidInteger(String, Span)
    UnexpectedToken(String, Span)
    UnexpectedEnd(Span)
    } derive(Eq,
    Debug
    )

    A fatal lexical or syntactic error.

    Annotation

    pub(all) struct Annotation {
    key : String
    value : String?
    } derive(Eq,
    Debug
    )

    One key/value annotation attached to an IDL item.

    BaseType

    pub(all) enum BaseType {
    Bool
    Byte
    I16
    I32
    I64
    Double
    String
    Binary
    Uuid
    Void
    } derive(Eq,
    Debug
    )

    Primitive types defined by the Thrift IDL.

    ChangeKind

    pub(all) enum ChangeKind {
    Compatible
    Warning
    Breaking
    } derive(Eq, ToJson,
    Debug
    )

    Compatibility classification for schema evolution.

    CompatibilityDirection

    pub(all) enum CompatibilityDirection {
    BackwardDirection
    ForwardDirection
    } derive(Eq,
    Debug
    )

    Direction in which a schema pair was compared.

    CompatibilityDirection::name

    CompatibilityFinding

    pub(all) struct CompatibilityFinding {
    source : String
    direction : CompatibilityDirection
    change : SchemaChange
    } derive(Eq,
    Debug
    )

    A schema-evolution finding with its source file and comparison direction.

    CompatibilityFormat

    pub(all) enum CompatibilityFormat {
    Text
    JsonFormat
    Markdown
    GithubAnnotations
    } derive(Eq,
    Debug
    )

    CompatibilityPolicy

    pub(all) enum CompatibilityPolicy {
    Backward
    Forward
    Full
    } derive(Eq,
    Debug
    )

    Backward checks the old schema against the new schema; forward checks the reverse direction; full requires both directions to pass.

    CompatibilityPolicy::name

    fn CompatibilityPolicy::name(self : CompatibilityPolicy) -> String

    ConstValue

    pub(all) enum ConstValue {
    BoolValue(Bool)
    IntegerValue(Int64)
    DoubleValue(Double)
    StringValue(String)
    IdentifierValue(String)
    ListValue(Array[ConstValue])
    MapValue(Array[(ConstValue, ConstValue)])
    } derive(Eq,
    Debug
    )

    A constant expression accepted by Thrift IDL.

    Definition

    pub(all) enum Definition {
    Const(name~ : String, ty~ : TypeRef, value~ : ConstValue, annotations~ : Array[Annotation], span~ : Span)
    Typedef(name~ : String, target~ : TypeRef, annotations~ : Array[Annotation], span~ : Span)
    Enum(name~ : String, members~ : Array[EnumMember], annotations~ : Array[Annotation], span~ : Span)
    Struct(name~ : String, fields~ : Array[Field], annotations~ : Array[Annotation], span~ : Span)
    Union(name~ : String, fields~ : Array[Field], annotations~ : Array[Annotation], span~ : Span)
    Exception(name~ : String, fields~ : Array[Field], annotations~ : Array[Annotation], span~ : Span)
    Service(name~ : String, extends~ : String?, functions~ : Array[FunctionDef], annotations~ : Array[Annotation], span~ : Span)
    } derive(Eq,
    Debug
    )

    Definition::kind

    fn Definition::kind(self : Definition) -> String

    Definition::name

    fn Definition::name(self : Definition) -> String

    Diagnostic

    pub(all) struct Diagnostic {
    code : String
    severity : Severity
    message : String
    span : Span
    } derive(Eq, ToJson,
    Debug
    )

    A stable, source-aware diagnostic produced by parsing or validation.

    Diagnostic::to_text

    fn Diagnostic::to_text(self : Diagnostic) -> String

    EnumMember

    pub(all) struct EnumMember {
    name : String
    value : Int
    annotations : Array[Annotation]
    span : Span
    } derive(Eq,
    Debug
    )

    Field

    pub(all) struct Field {
    id : Int
    requiredness : Requiredness
    ty : TypeRef
    name : String
    default_value : ConstValue?
    annotations : Array[Annotation]
    span : Span
    } derive(Eq,
    Debug
    )

    A field in a struct, union, exception, function, or throws clause.

    FunctionDef

    pub(all) struct FunctionDef {
    name : String
    return_type : TypeRef
    arguments : Array[Field]
    throws : Array[Field]
    oneway : Bool
    annotations : Array[Annotation]
    span : Span
    } derive(Eq,
    Debug
    )

    pub(all) enum Header {
    Include(path~ : String, span~ : Span)
    Namespace(language~ : String, name~ : String, span~ : Span)
    CppInclude(path~ : String, span~ : Span)
    } derive(Eq,
    Debug
    )

    Requiredness

    pub(all) enum Requiredness {
    Default
    Required
    Optional
    } derive(Eq,
    Debug
    )

    Schema

    pub(all) struct Schema {
    source : String
    headers : Array[Header]
    definitions : Array[Definition]
    } derive(Eq,
    Debug
    )

    A parsed Thrift file. Semantic diagnostics are produced separately by check_schema so tools can still inspect a syntactically valid AST.

    Schema::to_outline

    fn Schema::to_outline(self : Schema) -> String

    Produces a compact human-readable schema outline for CLIs and build logs.

    SchemaChange

    pub(all) struct SchemaChange {
    code : String
    kind : ChangeKind
    path : String
    message : String
    } derive(Eq, ToJson,
    Debug
    )

    One stable schema-evolution finding.

    SchemaChange::to_text

    fn SchemaChange::to_text(self : SchemaChange) -> String

    SchemaWorkspace

    pub(all) struct SchemaWorkspace {
    root : String
    documents : Array[Schema]
    } derive(Eq,
    Debug
    )

    A recursively loaded set of Thrift documents rooted at one entry schema. Documents are ordered deterministically with the root first, followed by includes in source order. Each normalized path is loaded at most once.

    Severity

    pub(all) enum Severity {
    Error
    Warning
    } derive(Eq, ToJson,
    Debug
    )

    Severity of a schema diagnostic.

    Span

    pub(all) struct Span {
    source : String
    start : Int
    end : Int
    line : Int
    column : Int
    } derive(Eq, ToJson,
    Debug
    )

    A half-open source range in a Thrift document.

    Span::synthetic

    fn Span::synthetic(source? : String) -> Span

    Creates a synthetic span for programmatically constructed declarations.

    Token

    pub(all) struct Token {
    kind : TokenKind
    span : Span
    } derive(Eq,
    Debug
    )

    One lexical token and its source location.

    Token::describe

    fn Token::describe(self : Token) -> String

    TokenKind

    pub(all) enum TokenKind {
    Identifier(String)
    StringLiteral(String)
    IntegerLiteral(Int64)
    FloatLiteral(Double)
    DocComment(String)
    Symbol(Char)
    End
    } derive(Eq,
    Debug
    )

    Token kinds emitted by the Thrift IDL lexer.

    TypeRef

    pub(all) enum TypeRef {
    Base(BaseType)
    Named(String)
    List(TypeRef)
    Set(TypeRef)
    Map(TypeRef, TypeRef)
    CppType(TypeRef, String)
    } derive(Eq,
    Debug
    )

    A primitive, named, or container type reference.

    check_schema

    fn check_schema(schema : Schema) -> Array[Diagnostic]

    Performs deterministic semantic checks without file-system access.

    Qualified names are accepted as references into an included schema. The caller can resolve and check include graphs separately.

    compare_schemas

    fn compare_schemas(before : Schema, after : Schema) -> Array[SchemaChange]

    Compares two checked schemas by stable field IDs, enum values, and service method names. Results are ordered deterministically by the old schema first, followed by additions from the new schema.

    compare_schemas_with_policy

    fn compare_schemas_with_policy(before : Schema, after : Schema, policy : CompatibilityPolicy, source? : String, suppressed? : Array[String]) -> Array[CompatibilityFinding]

    Applies an existing directional schema comparison in the selected policy. Findings keep their original MTHC codes and are ordered backward first, then forward. Suppression codes are exact matches (for example MTHC105).

    compile_idl

    fn compile_idl(input : String, source? : String) -> (Schema, Array[Diagnostic]) raise IdlError

    Parses and validates a document in one call.

    compile_workspace

    fn compile_workspace(root : String, loader : (String) -> String?) -> (SchemaWorkspace, Array[Diagnostic]) raise IdlError

    Loads, parses, and validates a complete Thrift include graph.

    Paths passed to loader use /, contain no . segments, and resolve .. relative to the including document. Missing sources and link failures are returned as diagnostics; lexical and syntactic failures raise IdlError.

    has_blocking_compatibility_findings

    fn has_blocking_compatibility_findings(findings : ArrayView[CompatibilityFinding]) -> Bool

    has_breaking_changes

    fn has_breaking_changes(changes : ArrayView[SchemaChange]) -> Bool

    has_errors

    fn has_errors(diagnostics : ArrayView[Diagnostic]) -> Bool

    lex

    fn lex(input : String, source? : String) -> Array[Token] raise IdlError

    Tokenizes one Thrift IDL document without accessing the file system.

    parse_idl

    fn parse_idl(input : String, source? : String) -> Schema raise IdlError

    Parses a complete Apache Thrift IDL document.

    render_compatibility_report

    fn render_compatibility_report(findings : ArrayView[CompatibilityFinding], policy : CompatibilityPolicy, format : CompatibilityFormat) -> String

    Renders deterministic machine or human output. Sources are logical schema paths; GitHub annotations intentionally omit line numbers because findings can describe a removed symbol that has no line in the new file.