use heck::{ToLowerCamelCase, ToUpperCamelCase};
use vox_types::{ServiceDescriptor, ShapeKind, classify_shape};
use super::types::{ts_type_server_arg, ts_type_server_return};
pub fn generate_handler_interface(service: &ServiceDescriptor) -> String {
let mut out = String::new();
let service_name = service.service_name.to_upper_camel_case();
out.push_str(&format!("// Handler interface for {service_name}\n"));
out.push_str(&format!("export interface {service_name}Handler {{\n"));
for method in service.methods {
let method_name = method.method_name.to_lower_camel_case();
let args = method
.args
.iter()
.map(|a| {
format!(
"{}: {}",
a.name.to_lower_camel_case(),
ts_type_server_arg(a.shape)
)
})
.collect::<Vec<_>>()
.join(", ");
let ret_ty = ts_type_server_return(method.return_shape);
out.push_str(&format!(
" {method_name}({args}): Promise<{ret_ty}> | {ret_ty};\n"
));
}
out.push_str("}\n\n");
out
}
pub fn generate_dispatcher_class(service: &ServiceDescriptor) -> String {
use crate::render::hex_u64;
let mut out = String::new();
let service_name = service.service_name.to_upper_camel_case();
let service_name_lower = service.service_name.to_lower_camel_case();
out.push_str(&format!("// Dispatcher for {service_name}\n"));
out.push_str(&format!(
"export class {service_name}Dispatcher implements Dispatcher {{\n"
));
out.push_str(&format!(
" private readonly handler: {service_name}Handler;\n\n"
));
out.push_str(&format!(
" constructor(handler: {service_name}Handler) {{\n"
));
out.push_str(" this.handler = handler;\n");
out.push_str(" }\n\n");
out.push_str(" getDescriptor(): ServiceDescriptor {\n");
out.push_str(&format!(" return {service_name_lower}_descriptor;\n"));
out.push_str(" }\n\n");
out.push_str(
" async dispatch(_context: RequestContext, method: MethodDescriptor, args: unknown[], call: VoxCall): Promise<void> {\n",
);
let mut first = true;
for method in service.methods {
let method_name = method.method_name.to_lower_camel_case();
let id = crate::method_id(method);
let is_fallible = matches!(
classify_shape(method.return_shape),
ShapeKind::Result { .. }
);
let typed_args: Vec<_> = method
.args
.iter()
.enumerate()
.map(|(i, a)| format!("args[{i}] as {}", ts_type_server_arg(a.shape)))
.collect();
let keyword = if first { "if" } else { "} else if" };
first = false;
out.push_str(&format!(
" {keyword} (method.id === {}n) {{\n",
hex_u64(id)
));
out.push_str(" try {\n");
out.push_str(&format!(
" const result = await this.handler.{method_name}({});\n",
typed_args.join(", ")
));
if is_fallible {
out.push_str(" if (result.ok) call.reply(result.value); else call.replyErr(result.error);\n");
} else {
out.push_str(" call.reply(result);\n");
}
out.push_str(" } catch (error) {\n");
out.push_str(
" call.replyInternalError(error instanceof Error ? error.message : String(error));\n",
);
out.push_str(" }\n");
}
if !first {
out.push_str(" }\n");
}
out.push_str(" }\n");
out.push_str("}\n\n");
out
}
pub fn generate_server(service: &ServiceDescriptor) -> String {
let mut out = String::new();
out.push_str(&generate_handler_interface(service));
out.push_str(&generate_dispatcher_class(service));
out
}