README

#exporters

Schema → String / Json / MoonBit source code 导出工具包。

本包提供多条独立导出管线(Prompt / JSON Schema / MoonBit struct / MoonZod Schema)。每条管线对 @core.SchemaType 的 17 个变体做逐一渲染,支持内联展开和命名引用两种模式。


#公开 API

#Prompt 导出

  • schema_to_prompt(schema) -> String — 将 Schema 渲染为 TypeScript-interface 风格的提示文本(全内联)
  • schema_to_prompt_named(schema, include_names?) -> String — 提取带 name 的 Schema 生成模块化接口,带拓扑排序和类型引用

#JSON Schema 导出

  • to_json_schema(schema) -> Json — 标准 JSON Schema(完整约束注释)
  • to_json_schema_skeleton(schema) -> Json — 轻量级骨架(仅类型结构,无约束)
  • to_json_schema_named(schema, include_names?) -> Json — 命名 Schema 导出为 $defs + $ref

#MoonBit 结构导出

  • schema_to_moonbit_struct(schema) -> String — 生成 MoonBit pub struct / pub enum 定义
  • schema_to_moonbit_struct_full(schema) -> String — 生成定义 + 静态 Type::to_schema() 方法

#代码生成

  • schema_to_moon_zod_code(schema) -> String — 将 Schema 反转为可 copy-paste 的 moon_zod 源码
  • schema_to_moon_zod_code_named(schema, include_names?) -> String — 带命名 Schema 引用的源码生成
  • schema_to_moon_zod_code_inline_with_refs(schema, include_names) -> String — 内联渲染,可选命名引用替换


#文件结构

exporters/ ├── prompt.mbt # schema_to_prompt / schema_to_prompt_named 公共 API ├── prompt_renderer.mbt # StringRenderer trait + NamedPromptRenderer 实现 ├── json_schema.mbt # to_json_schema / to_json_schema_named / to_json_schema_skeleton 公共 API ├── json_schema_renderer.mbt # JsonSchemaRenderer trait + NamedJsonRenderer 实现 ├── moonbit_struct.mbt # schema_to_moonbit_struct / _full 公共 API ├── schema_exporter.mbt # schema_to_moon_zod_code / _named / _inline 公共 API └── pkg.generated.mbti # moon info 生成的接口描述(勿手动编辑)


#实现说明

#渲染器架构

导出管线统一采用 trait 分发 模式:

  1. 每个 SchemaType 变体对应 trait 中的一个方法
  2. 顶层的 render_type / render_json_type 做单点分发
  3. 渲染器只负责「类型 → 文本/Json」的转换,约束注释、命名引用、描述文本由各模块独立处理

Schema ──► render_type(renderer, schema) ──► StringRenderer ──► String └── render_json_type(renderer, schema) ──► JsonSchemaRenderer ──► Json

#Prompt 导出(prompt.mbt + prompt_renderer.mbt

  • schema_to_prompt() 内部复用 NamedPromptRenderer([]),named 集合为空 → 全内联展开
  • schema_to_prompt_named() 收集所有带 name 的 Schema,拓扑排序后生成 export interface / export type 定义
  • include_names? 参数支持选择性导出(None = 全部,Some([]) = 无,Some([...]) = 指定)
  • named wrapper schemas(primitive、array、optional、default、transform、tuple、any、unknown、preprocess)统一生成 export type X = T
  • named intersection 当包含非 object 分支时输出 export type X = A & B,否则合并 object 字段

#JSON Schema 导出(json_schema.mbt + json_schema_renderer.mbt

  • NamedJsonRendererinclude_annotations 参数统一三种模式:
    • include_annotations=trueto_json_schema()(完整约束)
    • include_annotations=falseto_json_schema_skeleton()(仅结构)
  • optional() / default() 导出 nullable 语义:anyOf: [inner, {"type": "null"}]
  • Strip 模式导出 additionalProperties: false(与运行时幻觉防御对齐)
  • object intersection 合并为单个 closed object;同名字段用属性级 allOf 保留各自约束
  • $defs 渲染时排除自引用(ref_names.filter(n => n != ns.name)

#代码生成架构

本包包含两条反向代码生成管线,均不采用 trait 分发,而是直接 match SchemaType 生成目标语言源码。

#MoonBit 结构导出(moonbit_struct.mbt

  • schema_to_moonbit_struct()ObjectType 映射为 pub structEnumType 映射为 pub enum
  • schema_to_moonbit_struct_full() 额外生成静态 Type::to_schema() -> @moon_zod.Schema 方法,实现 struct ↔ Schema 往返
  • 基础类型映射:String → StringNumber → Int64 / Double(按 .int() 规则区分),Boolean → Bool
  • ArrayType → Array[T],可空 UnionType 剥除 null 后映射
  • AnyType / UnknownType 当前回退为 Json
  • TupleType / 非可空 UnionType / IntersectionType 当前回退为 Json // TODO
  • 字段名和类型名自动转义(MoonBit 关键字、保留字、首字母数字)
  • 约束注释以行内注释形式附加在 struct field 上

#MoonZod 代码导出(schema_exporter.mbt

  • schema_to_moon_zod_code() 将运行时 Schema 反转为可 copy-paste 的 moon_zod 方法链源码
  • 输出格式:let name = @moon_zod.string().min(3).describe("...").name("name")
  • 根 Schema 无 name 时自动赋予 "Root"
  • schema_to_moon_zod_code_named() 提取所有命名 Schema,拓扑排序后输出 let name = ... 定义列表
  • 规则注释通过 Rule.annotation 反推 .min() / .max() / .email() / .url() / .int()
  • json_to_literal() 将 Json 值转为 MoonBit 构造函数调用(Json::string(...) / Json::number(...) / true / false / null
  • PreprocessType / TransformType 输出占位闭包 fn(x) { Ok(x) },标记为最佳代码输出(自定义闭包无法序列化)
  • .required_error() / .invalid_type_error() 链式方法仍会输出代码,但当前 Schema 工厂 API 未暴露这两个参数——round-trip 不安全


#已知限制

  • 当前 moonbit_struct.mbtTupleType 仍回退为 Jsonany/unknown 仅做最低限度映射。待后续整体重构时重新评估。
  • MoonBit 结构导出尚不支持 tuple / intersection / union 等复杂类型的精确类型映射,当前回退为 Json // TODO
  • MoonZod 代码导出对 TransformType / PreprocessType 输出占位闭包 fn(x) { Ok(x) },自定义闭包无法序列化。

#
JsonSchemaRenderer

pub(open) trait JsonSchemaRenderer {
fn render_string(Self, schema :
Schema
) -> Json
fn render_number(Self, schema :
Schema
) -> Json
fn render_boolean(Self, schema :
Schema
) -> Json
fn render_null(Self, schema :
Schema
) -> Json
fn render_object(Self, spec : Map[String,
Schema
], mode :
ObjectMode
, schema :
Schema
) -> Json
fn render_array(Self, elem :
Schema
, schema :
Schema
) -> Json
fn render_optional(Self, inner :
Schema
, schema :
Schema
) -> Json
fn render_default(Self, inner :
Schema
, default_val : Json, schema :
Schema
) -> Json
fn render_enum(Self, values : Array[String], schema :
Schema
) -> Json
fn render_union(Self, schemas : Array[
Schema
], schema :
Schema
) -> Json
fn render_intersection(Self, schemas : Array[
Schema
], schema :
Schema
) -> Json
fn render_transform(Self, inner :
Schema
, closure :
TransformClosure
, schema :
Schema
) -> Json
fn render_literal(Self, value : Json, schema :
Schema
) -> Json
fn render_any(Self, schema :
Schema
) -> Json
fn render_unknown(Self, schema :
Schema
) -> Json
fn render_tuple(Self, items : Array[
Schema
], schema :
Schema
) -> Json
fn render_preprocess(Self, inner :
Schema
, schema :
Schema
) -> Json
}

Trait for rendering @core.Schema as a Json value (JSON @core.Schema export).

Each method corresponds to one @core.SchemaType variant and returns a Json value representing the JSON @core.Schema constraint.

#
StringRenderer

pub(open) trait StringRenderer {
fn render_string(Self, schema :
Schema
, indent : Int) -> String
fn render_number(Self, schema :
Schema
, indent : Int) -> String
fn render_boolean(Self, schema :
Schema
, indent : Int) -> String
fn render_null(Self, schema :
Schema
, indent : Int) -> String
fn render_object(Self, spec : Map[String,
Schema
], mode :
ObjectMode
, schema :
Schema
, indent : Int) -> String
fn render_array(Self, elem :
Schema
, schema :
Schema
, indent : Int) -> String
fn render_optional(Self, inner :
Schema
, schema :
Schema
, indent : Int) -> String
fn render_default(Self, inner :
Schema
, default_val : Json, schema :
Schema
, indent : Int) -> String
fn render_enum(Self, values : Array[String], schema :
Schema
, indent : Int) -> String
fn render_union(Self, schemas : Array[
Schema
], schema :
Schema
, indent : Int) -> String
fn render_intersection(Self, schemas : Array[
Schema
], schema :
Schema
, indent : Int) -> String
fn render_transform(Self, inner :
Schema
, closure :
TransformClosure
, schema :
Schema
, indent : Int) -> String
fn render_literal(Self, value : Json, schema :
Schema
, indent : Int) -> String
fn render_any(Self, schema :
Schema
, indent : Int) -> String
fn render_unknown(Self, schema :
Schema
, indent : Int) -> String
fn render_tuple(Self, items : Array[
Schema
], schema :
Schema
, indent : Int) -> String
fn render_preprocess(Self, inner :
Schema
, schema :
Schema
, indent : Int) -> String
}

Trait for rendering @core.Schema as a String (used by prompt.mbt and moonbit_struct.mbt).

Each method corresponds to one @core.SchemaType variant. The indent parameter controls nesting level for formatted output.

#
NamedJsonRenderer

pub struct NamedJsonRenderer {
named_names : Array[String]
include_annotations : Bool
}

#
NamedJsonRenderer::NamedJsonRenderer

fn NamedJsonRenderer::NamedJsonRenderer(named_names~ : Array[String], include_annotations? : Bool) -> NamedJsonRenderer

#
NamedPromptRenderer

pub struct NamedPromptRenderer {
named_schemas : Array[
Schema
]
}

Named prompt renderer: uses name references for named schemas.

#
NamedPromptRenderer::NamedPromptRenderer

#
render_json_type

fn[R : JsonSchemaRenderer] render_json_type(renderer : R, schema :
Schema
) -> Json

Dispatch to the appropriate JsonSchemaRenderer method based on @core.SchemaType.

#
render_json_type_ref

fn[R : JsonSchemaRenderer] render_json_type_ref(renderer : R, schema :
Schema
, named_names : Array[String]) -> Json

Named-aware version: emits a $ref if schema has a name in the list.

#
render_type

fn[R : StringRenderer] render_type(renderer : R, schema :
Schema
, indent : Int) -> String

Dispatch to the appropriate StringRenderer method based on @core.SchemaType.

#
render_type_named

fn[R : StringRenderer] render_type_named(renderer : R, schema :
Schema
, indent : Int, named_schemas : Array[
Schema
]) -> String

Named-aware version: renders a name reference if the schema has a name present in the named schemas list, otherwise dispatches normally.

#
schema_to_moon_zod_code

fn schema_to_moon_zod_code(schema :
Schema
) -> String

Generate best-effort moon_zod source code from a @core.Schema @core.object (inline expansion).

Example

let schema = @moon_zod.string().min(3).describe("username")

let code = schema_to_moon_zod_code(schema)
// "@moon_zod.string().min(3).describe(\"username\")"
If root schema has no name, it is assigned the name "Root".

#
schema_to_moon_zod_code_inline_with_refs

fn schema_to_moon_zod_code_inline_with_refs(schema :
Schema
, include_names : Array[String]?) -> String

Generate best-effort inline code with optional named schema reference substitution.

If include_names is None, renders all schemas inline. If include_names is Some(names), replaces references to named schemas with variable names.

For use with schema_to_moon_zod_code_named() which produces separate definitions.

#
schema_to_moon_zod_code_named

fn schema_to_moon_zod_code_named(schema :
Schema
, include_names? : Array[String]?) -> String

Generate best-effort separate named schema definitions and return as newline-separated list.

Example

let named_list = schema_to_moon_zod_code_named(root_schema, None) // Returns: // "User: @moon_zod.object({ ... }).name(\"User\")\n // Profile: @moon_zod.object({ ... }).name(\"Profile\")"
If root schema has no name, it is assigned the name "Root".

#
schema_to_moonbit_struct

fn schema_to_moonbit_struct(schema :
Schema
) -> String

Convert a moon_zod schema into MoonBit type definitions.

Every ObjectType in the schema tree is emitted as a standalone pub struct. Schema names are treated only as type-name hints; anonymous nested objects are named from their owner and field path.

#
schema_to_moonbit_struct_full

fn schema_to_moonbit_struct_full(schema :
Schema
) -> String

Convert a moon_zod schema into MoonBit type definitions plus static Type::to_schema() -> @moon_zod.Schema functions for each generated type.

#
schema_to_prompt

fn schema_to_prompt(schema :
Schema
) -> String

Public API: convert a schema to a TypeScript-interface-style prompt @core.string.

#
schema_to_prompt_named

fn schema_to_prompt_named(schema :
Schema
, include_names? : Array[String]?) -> String

Public API: convert a schema with named sub-schemas into a prompt with separate interface definitions and name-based references.

If include_names is None, exports all named schemas. If include_names is Some(names), exports only the specified schema names. If root schema has no name, it is assigned the name "Root".

#
to_json_schema

fn to_json_schema(schema :
Schema
) -> Json

Convert a moon_zod @core.Schema into a standard JSON @core.Schema @core.object with full constraint annotations (minLength, maximum, pattern, format, etc.).

#
to_json_schema_named

fn to_json_schema_named(schema :
Schema
, include_names? : Array[String]?) -> Json

Export a JSON @core.Schema with named sub-schemas in a $defs section. If root schema has no name, it is assigned the name "Root".

#
to_json_schema_skeleton

fn to_json_schema_skeleton(schema :
Schema
) -> Json

Convert a moon_zod @core.Schema into a lightweight JSON @core.Schema skeleton. Only structural type information is exported.