use std::io;
use std::sync::Arc;
use super::{CodeGeneratorConfig, Container, indent::IndentWrite};
use crate::reflection::format::{Format, Named, VariantFormat};
#[derive(Debug, Clone)]
pub struct EmitContext<'a> {
pub container: &'a Container<'a>,
pub config: &'a CodeGeneratorConfig,
pub variant: Option<VariantInfo<'a>>,
}
impl<'a> EmitContext<'a> {
#[must_use]
pub const fn top_level(container: &'a Container<'a>, config: &'a CodeGeneratorConfig) -> Self {
Self {
container,
config,
variant: None,
}
}
#[must_use]
pub const fn for_variant(
container: &'a Container<'a>,
config: &'a CodeGeneratorConfig,
variant: VariantInfo<'a>,
) -> Self {
Self {
container,
config,
variant: Some(variant),
}
}
#[must_use]
pub const fn is_variant(&self) -> bool {
self.variant.is_some()
}
#[must_use]
pub fn name(&self) -> &str {
match &self.variant {
Some(v) => v.name,
None => &self.container.name.name,
}
}
#[must_use]
pub fn fields(&self) -> Vec<Named<Format>> {
use crate::reflection::format::ContainerFormat;
if let Some(v) = &self.variant {
return v.fields.to_vec();
}
match self.container.format {
ContainerFormat::UnitStruct(_) | ContainerFormat::Enum(_, _) => vec![],
ContainerFormat::NewTypeStruct(format, _) => {
vec![Named::new(format, "value".to_string())]
}
ContainerFormat::TupleStruct(formats, _) => formats
.iter()
.enumerate()
.map(|(i, f)| Named::new(f, format!("field{i}")))
.collect(),
ContainerFormat::Struct(fields, _) => fields.clone(),
}
}
}
#[derive(Debug, Clone)]
pub struct VariantInfo<'a> {
pub name: &'a str,
pub index: usize,
pub format: &'a VariantFormat,
pub fields: &'a [Named<Format>],
pub parent_name: &'a str,
}
pub trait EmitterPlugin<L>: std::fmt::Debug {
fn imports(&self, _config: &CodeGeneratorConfig) -> Vec<String> {
vec![]
}
fn module_helpers(
&self,
_w: &mut dyn IndentWrite,
_config: &CodeGeneratorConfig,
) -> io::Result<()> {
Ok(())
}
fn type_annotations(&self, _ctx: &EmitContext) -> Vec<String> {
vec![]
}
fn type_conformances(&self, _ctx: &EmitContext) -> Vec<String> {
vec![]
}
fn has_type_body(&self, _ctx: &EmitContext) -> bool {
false
}
fn type_body_preamble(&self, _w: &mut dyn IndentWrite, _ctx: &EmitContext) -> io::Result<()> {
Ok(())
}
fn type_body(&self, _w: &mut dyn IndentWrite, _ctx: &EmitContext) -> io::Result<()> {
Ok(())
}
fn after_type(&self, _w: &mut dyn IndentWrite, _ctx: &EmitContext) -> io::Result<()> {
Ok(())
}
fn field_annotations(&self, _field: &Named<Format>, _ctx: &EmitContext) -> Vec<String> {
vec![]
}
fn enum_variant_annotations(&self, _name: &str) -> Vec<String> {
vec![]
}
fn runtime_files(&self) -> Vec<RuntimeFile> {
vec![]
}
fn manifest_dependencies(&self) -> Vec<String> {
vec![]
}
}
#[derive(Debug, Clone)]
pub struct RuntimeFile {
pub relative_path: String,
pub contents: Vec<u8>,
}
pub fn collect_from_plugins<L, F>(plugins: &[Arc<dyn EmitterPlugin<L>>], f: F) -> Vec<String>
where
F: Fn(&dyn EmitterPlugin<L>) -> Vec<String>,
{
plugins.iter().flat_map(|p| f(p.as_ref())).collect()
}
pub fn write_from_plugins<L, F>(
plugins: &[Arc<dyn EmitterPlugin<L>>],
w: &mut dyn IndentWrite,
f: F,
) -> io::Result<()>
where
F: Fn(&dyn EmitterPlugin<L>, &mut dyn IndentWrite) -> io::Result<()>,
{
for plugin in plugins {
f(plugin.as_ref(), w)?;
}
Ok(())
}
pub fn any_plugin<L, F>(plugins: &[Arc<dyn EmitterPlugin<L>>], f: F) -> bool
where
F: Fn(&dyn EmitterPlugin<L>) -> bool,
{
plugins.iter().any(|p| f(p.as_ref()))
}