use actr_web_protoc_codegen::{WebCodegen, WebCodegenConfig, WebCodegenRequest, descriptor};
use prost::Message;
use prost_types::compiler::CodeGeneratorRequest;
use std::io::{self, Read, Write};
use std::path::PathBuf;
fn main() -> anyhow::Result<()> {
let args: Vec<String> = std::env::args().collect();
if args.iter().any(|a| a == "--generate") {
return run_codegen_mode();
}
run_protoc_mode()
}
fn run_codegen_mode() -> anyhow::Result<()> {
let mut input = String::new();
io::stdin().read_to_string(&mut input)?;
let request: WebCodegenRequest = serde_json::from_str(&input)
.map_err(|e| anyhow::anyhow!("Failed to parse WebCodegenRequest: {e}"))?;
let response = actr_web_protoc_codegen::generate(&request);
let json = serde_json::to_string(&response)?;
io::stdout().write_all(json.as_bytes())?;
Ok(())
}
fn run_protoc_mode() -> anyhow::Result<()> {
let mut input = Vec::new();
io::stdin().read_to_end(&mut input)?;
let request = CodeGeneratorRequest::decode(&input[..])?;
let params = parse_parameters(request.parameter.as_deref().unwrap_or(""));
let mut services = Vec::new();
for proto_file in &request.proto_file {
let name = match &proto_file.name {
Some(n) => n,
None => continue,
};
if !request.file_to_generate.contains(name) {
continue;
}
match descriptor::file_to_proto_service(proto_file) {
Some(svc) => services.push(svc),
None => {
eprintln!("[protoc-gen-actr-web] {name}: no service found, skipping");
}
}
}
let config = WebCodegenConfig {
proto_files: Vec::new(),
rust_output_dir: PathBuf::from("/tmp/unused"),
ts_output_dir: params.output_dir.clone(),
generate_react_hooks: params.react_hooks,
includes: Vec::new(),
format_code: false, custom_templates_dir: None,
};
let codegen = WebCodegen::new(config);
let generated_files = codegen.generate_typescript_from_services(&services)?;
let mut response = prost_types::compiler::CodeGeneratorResponse::default();
for file in generated_files {
let mut gen_file = prost_types::compiler::code_generator_response::File::default();
let relative_path = file
.path
.strip_prefix(¶ms.output_dir)
.unwrap_or(&file.path);
gen_file.name = Some(relative_path.to_string_lossy().to_string());
gen_file.content = Some(file.content);
response.file.push(gen_file);
}
let mut output = Vec::new();
response.encode(&mut output)?;
io::stdout().write_all(&output)?;
Ok(())
}
struct PluginParams {
output_dir: PathBuf,
react_hooks: bool,
}
fn parse_parameters(params: &str) -> PluginParams {
let mut output_dir = PathBuf::from(".");
let mut react_hooks = false;
for param in params.split(',') {
if let Some(value) = param.strip_prefix("output=") {
output_dir = value.into();
} else if param == "react_hooks" {
react_hooks = true;
}
}
PluginParams {
output_dir,
react_hooks,
}
}