feat(simulation): NpcBlueprint struct design, RON schema, and validator CLI (#611)
Define ZoneSpec, CultureProfile, and NpcBlueprint structs with serde/RON deserialization. Ship example RON files as schema contract for the copy team (#609, #610). Add validate-ron CLI for copy team to lint their files without compiling the server. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,84 @@
|
||||
//! RON content validator CLI (#611).
|
||||
//!
|
||||
//! Deserializes a RON file into the actual Rust structs and prints errors.
|
||||
//! This is the copy team's lint tool — run it to check RON files without
|
||||
//! needing to compile the full server.
|
||||
//!
|
||||
//! # Usage
|
||||
//!
|
||||
//! ```sh
|
||||
//! # Via wrapper script (recommended):
|
||||
//! tooling/validate-ron content/global/zone-identity-spec.example.ron zone
|
||||
//! tooling/validate-ron content/global/culture-krenn.example.ron culture
|
||||
//!
|
||||
//! # Direct:
|
||||
//! cargo run --bin validate_ron -- <file.ron> <zone|culture>
|
||||
//! ```
|
||||
|
||||
use std::process;
|
||||
|
||||
use clap::Parser;
|
||||
|
||||
use settled_reach_server::npc::blueprint::{CultureProfile, ZoneSpec};
|
||||
|
||||
#[derive(Parser)]
|
||||
#[command(
|
||||
name = "validate_ron",
|
||||
about = "Validate RON content files against Rust struct schemas"
|
||||
)]
|
||||
struct Args {
|
||||
/// Path to the RON file to validate.
|
||||
file: String,
|
||||
/// Schema type: "zone" (ZoneSpec) or "culture" (CultureProfile).
|
||||
schema: String,
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let args = Args::parse();
|
||||
|
||||
let content = match std::fs::read_to_string(&args.file) {
|
||||
Ok(c) => c,
|
||||
Err(e) => {
|
||||
eprintln!("Error reading {}: {}", args.file, e);
|
||||
process::exit(1);
|
||||
}
|
||||
};
|
||||
|
||||
match args.schema.as_str() {
|
||||
"zone" => match ron::from_str::<ZoneSpec>(&content) {
|
||||
Ok(spec) => {
|
||||
println!("Valid ZoneSpec: {} ({})", spec.label, spec.zone_type);
|
||||
println!(" {} roles, {} social sites", spec.roles.len(), spec.social_sites.len());
|
||||
}
|
||||
Err(e) => {
|
||||
eprintln!("Invalid ZoneSpec in {}:", args.file);
|
||||
eprintln!(" {}", e);
|
||||
process::exit(1);
|
||||
}
|
||||
},
|
||||
"culture" => match ron::from_str::<CultureProfile>(&content) {
|
||||
Ok(profile) => {
|
||||
println!("Valid CultureProfile: {} ({})", profile.name, profile.id);
|
||||
println!(
|
||||
" {} given names, {} family names",
|
||||
profile.naming.given_names.len(),
|
||||
profile.naming.family_names.len()
|
||||
);
|
||||
println!(
|
||||
" {} filler words, {} favored traits",
|
||||
profile.speech.filler_words.len(),
|
||||
profile.values.favored_traits.len()
|
||||
);
|
||||
}
|
||||
Err(e) => {
|
||||
eprintln!("Invalid CultureProfile in {}:", args.file);
|
||||
eprintln!(" {}", e);
|
||||
process::exit(1);
|
||||
}
|
||||
},
|
||||
other => {
|
||||
eprintln!("Unknown schema type: '{}'. Use 'zone' or 'culture'.", other);
|
||||
process::exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user