use std::collections::HashSet;
use vexil_lang::ast::DefaultValue;
use vexil_lang::ir::{ConfigDef, ConfigFieldDef, ResolvedType, TypeDef, TypeId, TypeRegistry};
use crate::annotations::{emit_field_annotations, emit_type_annotations};
use crate::emit::CodeWriter;
use crate::types::rust_type;
pub fn emit_config(
w: &mut CodeWriter,
cfg: &ConfigDef,
registry: &TypeRegistry,
needs_box: &HashSet<(TypeId, usize)>,
) {
let name = cfg.name.as_str();
emit_type_annotations(w, &cfg.annotations);
w.line("#[derive(Debug, Clone, PartialEq)]");
w.open_block(&format!("pub struct {name}"));
for field in &cfg.fields {
emit_field_annotations(w, &field.annotations);
let field_rust_type = rust_type(&field.resolved_type, registry, needs_box, None);
w.line(&format!("pub {}: {},", field.name, field_rust_type));
}
w.close_block();
w.blank();
w.open_block(&format!("impl Default for {name}"));
w.open_block("fn default() -> Self");
w.open_block("Self");
for field in &cfg.fields {
let mut expr = default_value_expr(&field.default_value, field, registry);
if matches!(&field.resolved_type, ResolvedType::Optional(_))
&& !matches!(&field.default_value, DefaultValue::None)
{
expr = format!("Some({expr})");
}
w.line(&format!("{}: {},", field.name, expr));
}
w.close_block();
w.close_block();
w.close_block();
w.blank();
}
fn default_value_expr(
value: &DefaultValue,
field: &ConfigFieldDef,
registry: &TypeRegistry,
) -> String {
match value {
DefaultValue::None => "None".to_string(),
DefaultValue::Bool(b) => {
if *b {
"true".to_string()
} else {
"false".to_string()
}
}
DefaultValue::Int(n) => format!("{n}"),
DefaultValue::UInt(n) => format!("{n}"),
DefaultValue::Float(f) => format!("{f}"),
DefaultValue::Str(s) => format!("String::from(\"{s}\")"),
DefaultValue::Ident(name) => {
let type_name = resolve_type_name(&field.resolved_type, registry);
format!("{type_name}::{name}")
}
DefaultValue::UpperIdent(name) => {
let type_name = resolve_type_name(&field.resolved_type, registry);
format!("{type_name}::{name}")
}
DefaultValue::Array(items) => {
let exprs: Vec<String> = items
.iter()
.map(|spanned| default_value_expr(&spanned.node, field, registry))
.collect();
format!("vec![{}]", exprs.join(", "))
}
}
}
fn resolve_type_name(ty: &ResolvedType, registry: &TypeRegistry) -> String {
match ty {
ResolvedType::Named(id) => match registry.get(*id) {
Some(TypeDef::Enum(e)) => e.name.to_string(),
Some(TypeDef::Flags(f)) => f.name.to_string(),
Some(TypeDef::Newtype(n)) => n.name.to_string(),
Some(TypeDef::Message(m)) => m.name.to_string(),
Some(TypeDef::Union(u)) => u.name.to_string(),
Some(TypeDef::Config(c)) => c.name.to_string(),
_ => "UnresolvedType".to_string(),
},
ResolvedType::Optional(inner) => resolve_type_name(inner, registry),
_ => "UnresolvedType".to_string(),
}
}