CI / fmt (rustfmt --check) (push) Canceled after 0s
CI / clippy (ubuntu-latest) (push) Canceled after 0s
CI / clippy (windows-latest) (push) Canceled after 0s
CI / test (ubuntu-latest) (push) Canceled after 0s
CI / test (windows-latest) (push) Canceled after 0s
CI / build release (ubuntu-latest) (push) Canceled after 0s
CI / build release (windows-latest) (push) Canceled after 0s
Guards / core 零 UI 守门 (push) Canceled after 0s
Guards / 文件行数 ≤400 守门 (push) Canceled after 0s
Guards / 依赖许可与来源(cargo-deny) (push) Canceled after 0s
Rust 原生单文件 Minecraft 启动器:CLI 完整可用(install/launch/accounts/java/diagnose/agent), MCP server 接入外部 Agent 工具,GUI 开发中。
117 lines
3.6 KiB
Rust
117 lines
3.6 KiB
Rust
//! JSON Schema 构造helper。
|
||
//!
|
||
//! 工具参数 schema 一律**手写 JSON**(research/02 §6.2:`rig` 的 `DynamicTool::new` 与
|
||
//! rmcp 的 `ToolRoute::new_dyn` 吃的都是裸 `serde_json::Value`),所以不需要 schemars 派生。
|
||
//! 手写的好处是同一份 schema 直接喂两方,坏处是啰嗦——这个模块把啰嗦收敛到几个函数。
|
||
//!
|
||
//! 生成的 schema 一律按 **JSON Schema 2020-12** 的写法(rmcp 支持该版本;
|
||
//! `additionalProperties: false` 让模型少给野参数,[`super::validate`] 会执行它)。
|
||
|
||
use serde_json::{json, Map, Value};
|
||
|
||
/// 对象 schema。`properties` 按给定顺序写入(JSON 对象保序,UI 展示稳定)。
|
||
#[must_use]
|
||
pub fn object(properties: &[(&str, Value)], required: &[&str]) -> Value {
|
||
let mut props = Map::new();
|
||
for (name, schema) in properties {
|
||
props.insert((*name).to_string(), schema.clone());
|
||
}
|
||
json!({
|
||
"type": "object",
|
||
"properties": Value::Object(props),
|
||
"required": required,
|
||
"additionalProperties": false,
|
||
})
|
||
}
|
||
|
||
/// 任意 JSON 值(不校验类型)。
|
||
#[must_use]
|
||
pub fn any() -> Value {
|
||
json!({})
|
||
}
|
||
|
||
/// 字符串。
|
||
#[must_use]
|
||
pub fn string(description: &str) -> Value {
|
||
json!({ "type": "string", "description": description })
|
||
}
|
||
|
||
/// 非空字符串(`minLength: 1`,省掉一堆「路径不能为空」的运行时判断)。
|
||
#[must_use]
|
||
pub fn non_empty_string(description: &str) -> Value {
|
||
json!({ "type": "string", "description": description, "minLength": 1 })
|
||
}
|
||
|
||
/// 枚举字符串。
|
||
#[must_use]
|
||
pub fn string_enum(values: &[&str], description: &str) -> Value {
|
||
json!({ "type": "string", "description": description, "enum": values })
|
||
}
|
||
|
||
/// 布尔。
|
||
#[must_use]
|
||
pub fn boolean(description: &str) -> Value {
|
||
json!({ "type": "boolean", "description": description })
|
||
}
|
||
|
||
/// 带范围约束的整数。
|
||
#[must_use]
|
||
pub fn integer(description: &str, minimum: i64, maximum: i64) -> Value {
|
||
json!({
|
||
"type": "integer",
|
||
"description": description,
|
||
"minimum": minimum,
|
||
"maximum": maximum,
|
||
})
|
||
}
|
||
|
||
/// 字符串数组(可选条数上限)。
|
||
#[must_use]
|
||
pub fn string_array(description: &str, max_items: u64) -> Value {
|
||
json!({
|
||
"type": "array",
|
||
"description": description,
|
||
"items": { "type": "string" },
|
||
"maxItems": max_items,
|
||
})
|
||
}
|
||
|
||
/// 枚举字符串数组(如 `kinds: ["stderr","exit"]`)。
|
||
#[must_use]
|
||
pub fn enum_string_array(values: &[&str], description: &str, max_items: u64) -> Value {
|
||
json!({
|
||
"type": "array",
|
||
"description": description,
|
||
"items": { "type": "string", "enum": values },
|
||
"maxItems": max_items,
|
||
"uniqueItems": true,
|
||
})
|
||
}
|
||
|
||
#[cfg(test)]
|
||
mod tests {
|
||
use super::*;
|
||
use crate::registry::validate::validate;
|
||
|
||
#[test]
|
||
fn object_records_required_and_forbids_extras() {
|
||
let schema = object(
|
||
&[("path", non_empty_string("键路径")), ("value", any())],
|
||
&["path", "value"],
|
||
);
|
||
assert_eq!(schema["type"], json!("object"));
|
||
assert_eq!(schema["additionalProperties"], json!(false));
|
||
assert_eq!(schema["required"], json!(["path", "value"]));
|
||
assert!(validate(&schema, &json!({ "path": "a", "value": 1 })).is_ok());
|
||
}
|
||
|
||
#[test]
|
||
fn integer_builder_carries_bounds() {
|
||
let schema = integer("条数", 1, 500);
|
||
assert_eq!(schema["minimum"], json!(1));
|
||
assert_eq!(schema["maximum"], json!(500));
|
||
assert!(validate(&schema, &json!(0)).is_err());
|
||
assert!(validate(&schema, &json!(500)).is_ok());
|
||
}
|
||
}
|