use crate::ir::{ApiKind, ApiSpec};
pub fn render(spec: &ApiSpec) -> String {
let needs_tonic = matches!(spec.kind, ApiKind::Grpc);
let needs_http = matches!(spec.kind, ApiKind::OpenApi | ApiKind::GraphQl);
let mut deps = String::new();
deps.push_str("serde = { version = \"1\", features = [\"derive\"] }\n");
deps.push_str("serde_json = \"1\"\n");
deps.push_str("clap = { version = \"4\", features = [\"derive\", \"env\"] }\n");
deps.push_str("tokio = { version = \"1\", features = [\"full\"] }\n");
deps.push_str("anyhow = \"1\"\n");
if needs_http {
deps.push_str("reqwest = { version = \"0.12\", features = [\"json\", \"rustls-tls\"], default-features = false }\n");
}
if spec.operations.iter().any(|op| op.streaming.is_streaming()) {
deps.push_str("futures-util = \"0.3\"\n");
if spec.kind == ApiKind::GraphQl {
deps.push_str("tokio-tungstenite = { version = \"0.23\", features = [\"rustls-tls-native-roots\"] }\n");
}
}
if needs_tonic {
deps.push_str(
"# gRPC scaffold — wire up `tonic` + `prost` once you compile the .proto file.\n",
);
deps.push_str("tonic = \"0.12\"\n");
deps.push_str("prost = \"0.13\"\n");
deps.push_str("tokio-stream = { version = \"0.1\", features = [\"net\"] }\n");
}
let bin_name = format!("{}-cli", spec.name.replace('_', "-"));
let build_script = if needs_tonic {
"\nbuild = \"build.rs\""
} else {
""
};
let build_deps = if needs_tonic {
"\n[build-dependencies]\ntonic-build = \"0.12\"\n"
} else {
""
};
format!(
r#"# Generated by oxide-gen. Edit at your own risk.
[package]
name = "{name}"
version = "{version}"
edition = "2021"
description = "{description}"{build_script}
[lib]
name = "{lib_name}"
path = "src/lib.rs"
[[bin]]
name = "{bin_name}"
path = "src/main.rs"
[dependencies]
{deps}
{build_deps}"#,
name = spec.name.replace('_', "-"),
version = spec.version,
description = escape_toml(spec.description.as_deref().unwrap_or(&spec.display_name)),
lib_name = spec.name,
bin_name = bin_name,
deps = deps.trim_end(),
build_script = build_script,
build_deps = build_deps,
)
}
fn escape_toml(s: &str) -> String {
s.replace('\\', "\\\\").replace('"', "\\\"")
}