use std::env;
use std::fs;
use std::path::Path;
fn main() {
let json_path = Path::new(env!("CARGO_MANIFEST_DIR")).join("plans.json");
println!("cargo:rerun-if-changed={}", json_path.display());
let src = fs::read_to_string(&json_path).expect("read plans.json");
let v: serde_json::Value = serde_json::from_str(&src).expect("parse plans.json");
let rules = v["nameRules"].as_array().expect("nameRules array");
let caps = v["caps"].as_object().expect("caps object");
let plans = v["plans"].as_array().expect("plans array");
let mut out = String::from("// @generated by build.rs from plans.json — do not edit.\n");
out.push_str("pub static NAME_RULES: &[(&[&str], &str)] = &[\n");
for r in rules {
let needles: Vec<String> = r["needles"]
.as_array()
.expect("needles array")
.iter()
.map(|n| format!("\"{}\"", n.as_str().expect("needle string")))
.collect();
out.push_str(&format!(
" (&[{}], \"{}\"),\n",
needles.join(", "),
r["name"].as_str().expect("rule name")
));
}
out.push_str("];\n");
out.push_str(&format!(
"pub const DEFAULT_NAME: &str = \"{}\";\n",
v["defaultName"].as_str().expect("defaultName")
));
out.push_str("pub static CAPS: &[(&str, Option<f64>)] = &[\n");
for (name, cap) in caps {
let value = if cap.is_null() {
"None".to_string()
} else {
format!("Some({:.1})", cap.as_f64().expect("numeric cap"))
};
out.push_str(&format!(" (\"{name}\", {value}),\n"));
}
out.push_str("];\n");
out.push_str("pub static PLANS: &[(&str, &str, &str, &str, &str)] = &[\n");
for p in plans {
out.push_str(&format!(
" (\"{}\", \"{}\", \"{}\", \"{}\", \"{}\"),\n",
p["name"].as_str().expect("plan name"),
p["price"].as_str().expect("plan price"),
p["monthly"].as_str().expect("plan monthly"),
p["fiveHour"].as_str().expect("plan fiveHour"),
p["weekly"].as_str().expect("plan weekly"),
));
}
out.push_str("];\n");
let gating_path = Path::new(env!("CARGO_MANIFEST_DIR")).join("gating.json");
println!("cargo:rerun-if-changed={}", gating_path.display());
let gsrc = fs::read_to_string(&gating_path).expect("read gating.json");
let g: serde_json::Value = serde_json::from_str(&gsrc).expect("parse gating.json");
fn str_slice(a: &serde_json::Value) -> String {
let items: Vec<String> = a
.as_array()
.expect("array")
.iter()
.map(|s| format!("\"{}\"", s.as_str().expect("string")))
.collect();
format!("&[{}]", items.join(", "))
}
out.push_str("pub static GATE_CATEGORIES: &[(&str, &str)] = &[\n");
for (model, cat) in g["categories"].as_object().expect("categories") {
out.push_str(&format!(
" (\"{model}\", \"{}\"),\n",
cat.as_str().expect("category")
));
}
out.push_str("];\n");
out.push_str("pub static GATE_PLANS: &[(&str, &[&str], &[&str])] = &[\n");
for (plan, rule) in g["plans"].as_object().expect("plans") {
out.push_str(&format!(
" (\"{plan}\", {}, {}),\n",
str_slice(&rule["allowedCategories"]),
str_slice(&rule["blockedModels"]),
));
}
out.push_str("];\n");
out.push_str("pub static GATE_HARD_BLOCKED: &[(&str, &[&str])] = &[\n");
for (plan, list) in g["hardBlocked"].as_object().expect("hardBlocked") {
out.push_str(&format!(" (\"{plan}\", {}),\n", str_slice(list)));
}
out.push_str("];\n");
out.push_str("pub static GATE_KNOWN: &[&str] = ");
out.push_str(&str_slice(&g["knownModels"]));
out.push_str(";\n");
out.push_str(&format!(
"pub const GATE_EXTRACTED_AT: &str = \"{}\";\n",
g["extractedAt"].as_str().unwrap_or("")
));
out.push_str(&format!(
"pub const GATE_CLI_VERSION: &str = \"{}\";\n",
g["cliVersion"].as_str().unwrap_or("")
));
out.push_str("pub static GATE_ALIASES: &[(&str, &str)] = &[\n");
for (from, to) in g["aliases"].as_object().expect("aliases") {
out.push_str(&format!(
" (\"{from}\", \"{}\"),\n",
to.as_str().expect("alias target")
));
}
out.push_str("];\n");
let dest = Path::new(&env::var("OUT_DIR").expect("OUT_DIR")).join("plans.rs");
fs::write(dest, out).expect("write plans.rs");
}