use convert_case::{Casing, Case};
use cronus_spec::{RawUsecase, RawUsecaseMethod, RawUsecaseMethodRestOption, RawSchema};
use crate::{Generator, Ctxt};
use anyhow::{Ok, Result};
pub struct TypescriptNestjsGenerator {
}
impl TypescriptNestjsGenerator {
pub fn new() -> Self {
Self {
}
}
}
impl Generator for TypescriptNestjsGenerator {
fn name(&self) -> &'static str {
return "typescript_nestjs"
}
fn generate_usecase(&self, ctx: &Ctxt, name: &str, usecase: &RawUsecase) -> Result<()> {
let mut nestjs_code = String::new();
nestjs_code.push_str(&format!("@Controller('/{}')\n", name.to_lowercase()));
nestjs_code.push_str(&format!("export class {}Controller {{\n", name.to_case(Case::UpperCamel)));
for (usecase_name, usecase_method) in &usecase.methods {
if let Some(options) = &usecase_method.option {
if let Some(rest_option) = &options.rest {
nestjs_code.push_str(&self.generate_method(usecase_name, usecase_method, rest_option));
}
}
}
nestjs_code.push_str("}\n\n");
ctx.append_file(self.name(), &self.dst(ctx), &nestjs_code);
Ok(())
}
}
impl TypescriptNestjsGenerator {
fn generate_method(&self, usecase_name: &str, method: &RawUsecaseMethod, rest_option: &RawUsecaseMethodRestOption) -> String {
let mut method_code = String::new();
method_code.push_str(&format!(" @{}('{}')\n", &rest_option.method.to_case(Case::UpperCamel), rest_option.path.clone().unwrap_or("".to_string())));
method_code.push_str(&format!(" async {}() {{\n", usecase_name.to_case(Case::Camel)));
method_code.push_str(" // Handler logic here\n");
method_code.push_str(" }\n");
method_code
}
fn dst(&self, ctx: &Ctxt) -> String {
if let Some(gen_config) = &ctx.spec.option.as_ref().unwrap().generator {
if let Some(tsnestjs_gen_config) = &gen_config.typescript_nestjs {
if let Some(file) = &tsnestjs_gen_config.file {
return file.clone()
}
}
}
return "controller.ts".to_string();
}
pub fn generate_dto(&self, schema: &RawSchema, dto_name: &str) -> String {
let mut dto_code = format!("export class {} {{\n", dto_name);
if let Some(ty) = &schema.ty {
dto_code.push_str(&map_field("type", schema, false));
}
if let Some(items) = &schema.items {
let nested_dto_name = format!("{}Item", dto_name);
dto_code.push_str(&self.generate_dto(items, &nested_dto_name));
dto_code.push_str(&format!(" items: {}[];\n", nested_dto_name));
}
if let Some(properties) = &schema.properties {
for (key, prop_schema) in properties {
let is_optional = prop_schema.required.unwrap_or(false);
dto_code.push_str(&map_field(key, prop_schema, is_optional));
}
}
if let Some(enum_items) = &schema.enum_items {
}
dto_code.push_str("}\n\n");
dto_code
}
}
fn map_field(field_name: &str, schema: &RawSchema, is_optional: bool) -> String {
let ts_type = match schema.ty.as_deref() {
Some("string") => "string",
Some("number") => "number",
Some("boolean") => "boolean",
Some("object") => "any", Some("array") => "Array<any>", _ => "any"
};
format!(" {}: {}{};\n", field_name, ts_type, if is_optional { "?" } else {""})
}