use super::{
super::init::Language,
SchemaGenerator,
utils::{derive_class_name, infer_sql_source, map_graphql_to_lang, to_camel_case},
};
use crate::schema::intermediate::{
IntermediateEnum, IntermediateQuery, IntermediateSchema, IntermediateType,
};
pub(super) struct JavaGenerator;
impl SchemaGenerator for JavaGenerator {
fn generate(&self, schema: &IntermediateSchema) -> String {
let mut out = String::new();
for enum_def in &schema.enums {
generate_java_enum(&mut out, enum_def);
}
for ty in &schema.types {
generate_java_type(&mut out, ty);
}
for query in &schema.queries {
generate_java_query(&mut out, query);
}
while out.ends_with("\n\n") {
out.pop();
}
if !out.ends_with('\n') {
out.push('\n');
}
out
}
}
fn generate_java_enum(out: &mut String, enum_def: &IntermediateEnum) {
out.push_str(&format!("public enum {} {{\n", enum_def.name));
let names: Vec<&str> = enum_def.values.iter().map(|v| v.name.as_str()).collect();
out.push_str(&format!(" {}\n", names.join(", ")));
out.push_str("}\n\n");
}
fn generate_java_type(out: &mut String, ty: &IntermediateType) {
let sql_source = infer_sql_source(&ty.name);
out.push_str(&format!("@Type(sqlSource = \"{sql_source}\")\n"));
out.push_str(&format!("public record {}(\n", ty.name));
for (i, field) in ty.fields.iter().enumerate() {
let lang_type = map_graphql_to_lang(Language::Java, &field.field_type);
let field_name = to_camel_case(&field.name);
let nullable_prefix = if field.nullable { "@Nullable " } else { "" };
let comma = if i + 1 < ty.fields.len() { "," } else { "" };
out.push_str(&format!(" {nullable_prefix}{lang_type} {field_name}{comma}\n"));
}
out.push_str(") {}\n\n");
}
fn generate_java_query(out: &mut String, query: &IntermediateQuery) {
let sql_source = query.sql_source.as_deref().unwrap_or("v_unknown");
let class_name = derive_class_name(query);
let mut params = vec![format!("returnType = {}.class", query.return_type)];
if query.returns_list {
params.push("returnArray = true".to_string());
}
params.push(format!("sqlSource = \"{sql_source}\""));
if !query.arguments.is_empty() {
let arg_strs: Vec<String> = query
.arguments
.iter()
.map(|a| {
let required = if a.nullable { "false" } else { "true" };
format!(
"args = @Arg(name = \"{}\", type = \"{}\", required = {required})",
a.name, a.arg_type
)
})
.collect();
params.extend(arg_strs);
}
out.push_str(&format!("@Query({})\n", params.join(", ")));
out.push_str(&format!("public interface {class_name} {{}}\n\n"));
}