| Crates.io | herolib-toschema |
| lib.rs | herolib-toschema |
| version | 0.1.0 |
| created_at | 2025-12-18 06:30:45.474939+00 |
| updated_at | 2025-12-18 06:30:45.474939+00 |
| description | Derive macros for JSON Schema generation and HeroScript serialization |
| homepage | |
| repository | https://github.com/threefoldtech/sal |
| max_upload_size | |
| id | 1991808 |
| size | 91,522 |
A procedural macro that automatically generates JSON Schema from Rust structs. Nested structs are fully inlined, producing a complete self-contained schema.
Add to your Cargo.toml:
[dependencies]
toschema = { path = "../toschema" } # or appropriate path
Then derive ToSchema on your structs:
use toschema::ToSchema;
#[derive(ToSchema)]
struct Inner {
value: i32,
}
#[derive(ToSchema)]
struct MyConfig {
name: String,
count: u32,
enabled: bool,
items: Vec<String>,
optional_field: Option<String>,
nested: Inner, // Fully inlined in output
}
fn main() {
// Get compact JSON Schema
let schema = MyConfig::json_schema();
println!("{}", schema);
// Get pretty-printed JSON Schema
let schema_pretty = MyConfig::json_schema_pretty();
println!("{}", schema_pretty);
}
For a complex struct with nested types, the schema is fully inlined:
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "NodeSpec",
"type": "object",
"properties": {
"name": {
"type": "string"
},
"hardware": {
"type": "object",
"properties": {
"hardware_cost_usd": {
"type": "number"
},
"power_consumption_watts": {
"type": "number"
},
"rack_units": {
"type": "number"
}
},
"required": [
"hardware_cost_usd",
"power_consumption_watts",
"rack_units"
]
},
"capacity": {
"type": "object",
"properties": {
"memory_gb": {
"type": "integer"
},
"vcpu": {
"type": "integer"
},
"ssd_gb": {
"type": "integer"
},
"hdd_gb": {
"type": "integer"
},
"nr_gpu": {
"type": "integer"
},
"passmark": {
"type": "integer"
},
"llm_tps_performance_70b": {
"type": "integer"
}
},
"required": [
"memory_gb",
"vcpu",
"ssd_gb",
"hdd_gb",
"nr_gpu",
"passmark",
"llm_tps_performance_70b"
]
},
"agents": {
"type": "object",
"properties": {
"max_agents": {
"type": "integer"
},
"agent_type": {
"type": "string"
}
},
"required": [
"max_agents",
"agent_type"
]
},
"slices": {
"type": "object",
"properties": {
"nr_slices": {
"type": "integer"
}
},
"required": [
"nr_slices"
]
},
"pricing": {
"type": "object",
"properties": {
"node_price_per_slice_per_month": {
"type": "number"
},
"agent_price_per_month": {
"type": "number"
},
"hdd_price_per_gb_per_month": {
"type": "number"
}
},
"required": [
"node_price_per_slice_per_month",
"agent_price_per_month",
"hdd_price_per_gb_per_month"
]
}
},
"required": [
"name",
"hardware",
"capacity",
"agents",
"slices",
"pricing"
]
}
| Rust Type | JSON Schema Type |
|---|---|
String, &str |
"string" |
bool |
"boolean" |
i8, i16, i32, i64, i128, isize |
"integer" |
u8, u16, u32, u64, u128, usize |
"integer" |
f32, f64 |
"number" |
Vec<T> |
"array" with items |
Option<T> |
Type with ["type", "null"] |
HashMap<K, V>, BTreeMap<K, V> |
"object" with additionalProperties |
| Custom structs | Fully inlined object schema |
| Enums (unit variants) | "enum": ["Variant1", "Variant2", ...] |
| Enums (with data) | "oneOf": [...] with variant schemas |
Enums are fully supported with proper JSON Schema generation:
#[derive(ToSchema)]
enum Color {
Red,
Green,
Blue,
}
// Generates:
// {
// "$schema": "http://json-schema.org/draft-07/schema#",
// "title": "Color",
// "enum": ["Red", "Green", "Blue"]
// }
Enums with tuple or struct variants use oneOf:
#[derive(ToSchema)]
enum Message {
Quit, // Unit variant -> {"const": "Quit"}
Move { x: i32, y: i32 }, // Struct variant -> object with "Move" property
Write(String), // Single tuple variant -> object with "Write" property
ChangeColor(i32, i32, i32), // Multi-element tuple -> object with array
}
// Generates:
// {
// "$schema": "http://json-schema.org/draft-07/schema#",
// "title": "Message",
// "oneOf": [
// {"const": "Quit"},
// {"type": "object", "properties": {"Move": {"type": "object", "properties": {"x": {"type": "integer"}, "y": {"type": "integer"}}, "required": ["x", "y"]}}, "required": ["Move"]},
// {"type": "object", "properties": {"Write": {"type": "string"}}, "required": ["Write"]},
// {"type": "object", "properties": {"ChangeColor": {"type": "array", "items": [...], "minItems": 3, "maxItems": 3}}, "required": ["ChangeColor"]}
// ]
// }
Enums can be used as field types in structs:
#[derive(ToSchema)]
enum Status {
Pending,
Active,
Completed,
}
#[derive(ToSchema)]
struct Task {
name: String,
status: Status, // Enum field - schema is fully inlined
}
The derive macro adds these methods to your struct:
fn json_schema() -> String - Returns compact JSON Schema with $schema headerfn json_schema_pretty() -> String - Returns formatted JSON Schema with indentationfn schema_object() -> String - Returns just the object schema (for embedding)When using this crate to generate schemas for AI agents:
Add the derive macro to any struct that needs schema generation:
use toschema::ToSchema;
#[derive(ToSchema)]
struct YourStruct { ... }
Call json_schema_pretty() to get a human-readable schema:
let schema = YourStruct::json_schema_pretty();
For nested structs, each struct needs its own #[derive(ToSchema)]:
#[derive(ToSchema)]
struct Inner {
value: i32,
}
#[derive(ToSchema)]
struct Outer {
name: String,
inner: Inner, // Will be FULLY INLINED in the schema
}
Optional fields are automatically detected and excluded from required:
#[derive(ToSchema)]
struct Config {
required_field: String, // In "required" array
optional_field: Option<i32>, // NOT in "required" array, type is ["integer", "null"]
}
The schema is self-contained - all nested types are fully inlined, no $ref pointers.
This makes the schema directly usable by AI tools without needing to resolve references.
The schema follows JSON Schema draft-07 and can be used with any JSON Schema validator or AI tool that accepts JSON Schema definitions.