use std::io::{Result, Write};
use std::sync::Arc;
use crate::{
Registry,
generation::{
CodeGenerator, CodeGeneratorConfig, Container, Emitter, csharp::emitter::CSharp,
indent::IndentedWriter, module::Module, plugin::EmitterPlugin,
},
reflection::format::{Format, FormatHolder, Namespace, QualifiedTypeName},
};
pub struct CSharpCodeGenerator<'a> {
pub(crate) config: &'a CodeGeneratorConfig,
pub(crate) plugins: Vec<Arc<dyn EmitterPlugin<CSharp>>>,
}
impl<'a> CodeGenerator<'a> for CSharpCodeGenerator<'a> {
fn new(config: &'a CodeGeneratorConfig) -> Self {
Self {
config,
plugins: vec![],
}
}
fn write_output<W: Write>(&mut self, writer: &mut W, registry: &Registry) -> Result<()> {
self.output(writer, registry)
}
}
impl<'a> CSharpCodeGenerator<'a> {
#[must_use]
pub fn new(config: &'a CodeGeneratorConfig) -> Self {
Self {
config,
plugins: vec![],
}
}
#[must_use]
pub fn with_plugins(mut self, plugins: Vec<Arc<dyn EmitterPlugin<CSharp>>>) -> Self {
self.plugins = plugins;
self
}
pub fn output(&self, out: &mut impl Write, registry: &Registry) -> Result<()> {
let w = &mut IndentedWriter::new(out, self.config.indent);
let mut config = self.config.clone();
config.update_from(registry);
let updated_registry = Self::update_qualified_names(&config, registry);
let mut lang = CSharp::new(&config, &updated_registry);
for p in &self.plugins {
lang = lang.with_plugin(p.clone());
}
Module::new(&config).write(w, &lang)?;
for (index, container) in updated_registry.iter().map(Container::from).enumerate() {
if index > 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 current_leaf_namespace = config
.module_name()
.rsplit_once('.')
.map_or_else(|| config.module_name(), |(_, leaf)| leaf);
if namespace == current_leaf_namespace {
*qualified_name =
QualifiedTypeName::root(qualified_name.name.clone());
} else {
*qualified_name = QualifiedTypeName::namespaced(
format!("{}.{}", config.module_name(), namespace),
qualified_name.name.clone(),
);
}
}
Namespace::Root => {
if config.module_name().contains('.') {
*qualified_name = QualifiedTypeName::namespaced(
config.module_name().to_string(),
qualified_name.name.clone(),
);
}
}
}
}
Ok(())
});
}
updated_registry
}
}
#[cfg(test)]
mod tests;