use std::io::{Result, Write};
use crate::{
Registry,
generation::{
CodeGen, CodeGeneratorConfig, Container, Emitter,
config::PackageLocation,
indent::{IndentConfig, IndentedWriter},
kotlin::emitter::Kotlin,
module::Module,
},
reflection::format::{Format, FormatHolder, Namespace, QualifiedTypeName},
};
pub struct CodeGenerator<'a> {
pub(crate) config: &'a CodeGeneratorConfig,
}
impl<'a> CodeGen<'a> for CodeGenerator<'a> {
fn new(config: &'a CodeGeneratorConfig) -> Self {
CodeGenerator::new(config)
}
fn write_output<W: std::io::Write>(
&mut self,
writer: &mut W,
registry: &Registry,
) -> Result<()> {
self.output(writer, registry)
}
}
impl<'a> CodeGenerator<'a> {
#[must_use]
pub fn new(config: &'a CodeGeneratorConfig) -> Self {
Self { config }
}
pub fn output(&self, out: &mut impl Write, registry: &Registry) -> Result<()> {
let w = &mut IndentedWriter::new(out, IndentConfig::Space(4));
let mut config = self.config.clone();
config.update_from(registry);
let lang = Kotlin::new(config.encoding);
Module::new(&config).write(w, lang)?;
for (i, container) in Self::update_qualified_names(&config, registry)
.iter()
.map(Container::from)
.enumerate()
{
if i > 0 {
writeln!(w)?;
}
container.write(w, lang)?;
}
Ok(())
}
fn update_qualified_names(config: &CodeGeneratorConfig, registry: &Registry) -> Registry {
let mut updated_registry = registry.clone();
for container_format in updated_registry.values_mut() {
let _ = container_format.visit_mut(&mut |format| {
if let Format::TypeName(qualified_name) = format {
match &qualified_name.namespace {
Namespace::Named(namespace) => {
let namespace = namespace.clone();
let external_package_handled = if let Some(external_package) =
config.external_packages.get(&namespace)
{
if let PackageLocation::Path(path) = &external_package.location {
let full_namespace = format!("{path}.{namespace}");
*qualified_name = QualifiedTypeName::namespaced(
full_namespace,
qualified_name.name.clone(),
);
true
} else {
false
}
} else {
false
};
if !external_package_handled {
let current_leaf_namespace = config
.module_name()
.rsplit_once('.')
.map_or(config.module_name(), |(_, leaf)| leaf);
if config.external_definitions.contains_key(&namespace)
&& namespace != current_leaf_namespace
{
let full_namespace =
format!("{}.{namespace}", config.module_name());
*qualified_name = QualifiedTypeName::namespaced(
full_namespace,
qualified_name.name.clone(),
);
} else if namespace == current_leaf_namespace {
*qualified_name = QualifiedTypeName::namespaced(
config.module_name().to_string(),
qualified_name.name.clone(),
);
} else {
let full_namespace =
format!("{}.{namespace}", config.module_name());
*qualified_name = QualifiedTypeName::namespaced(
full_namespace,
qualified_name.name.clone(),
);
}
}
}
Namespace::Root => {
*qualified_name = QualifiedTypeName::namespaced(
config.module_name().to_string(),
qualified_name.name.clone(),
);
}
}
}
Ok(())
});
}
updated_registry
}
}
#[cfg(test)]
mod tests;