use super::{BuildConfig, build_all_configs, filter_for_typesync, merge_tables_and_objects};
use crate::error::EvenframeError;
use crate::types::{ForeignTypeRegistry, StructConfig, TaggedUnion};
#[cfg(feature = "flatbuffers")]
use crate::typesync::flatbuffers::generate_flatbuffers_schema_string;
#[cfg(feature = "macroforge")]
use crate::typesync::macroforge::generate_macroforge_type_string;
#[cfg(feature = "protobuf")]
use crate::typesync::protobuf::generate_protobuf_schema_string;
use crate::typesync::{
arktype::generate_arktype_type_string, effect::generate_effect_schema_string,
};
use std::collections::HashMap;
use std::fs;
use std::path::PathBuf;
use tracing::{debug, info};
type TypesyncConfigs = (HashMap<String, TaggedUnion>, HashMap<String, StructConfig>);
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum GeneratorType {
ArkType,
Effect,
Macroforge,
FlatBuffers,
Protobuf,
}
impl GeneratorType {
pub fn default_filename(&self) -> &'static str {
match self {
GeneratorType::ArkType => "arktype.ts",
GeneratorType::Effect => "bindings.ts",
GeneratorType::Macroforge => "macroforge.ts",
GeneratorType::FlatBuffers => "schema.fbs",
GeneratorType::Protobuf => "schema.proto",
}
}
}
#[derive(Debug, Clone)]
pub struct GeneratedFile {
pub path: PathBuf,
pub bytes_written: usize,
pub generator_type: GeneratorType,
}
#[derive(Debug, Clone)]
pub struct GenerationReport {
pub files: Vec<GeneratedFile>,
pub enums_processed: usize,
pub structs_processed: usize,
pub tables_processed: usize,
}
impl GenerationReport {
fn new() -> Self {
Self {
files: Vec::new(),
enums_processed: 0,
structs_processed: 0,
tables_processed: 0,
}
}
fn add_file(&mut self, file: GeneratedFile) {
self.files.push(file);
}
}
pub struct TypeGenerator {
config: BuildConfig,
}
impl TypeGenerator {
pub fn new(config: BuildConfig) -> Self {
Self { config }
}
fn build_typesync_configs(&self) -> Result<TypesyncConfigs, EvenframeError> {
let (enums, tables, objects) = build_all_configs(&self.config)?;
let (enums, tables, objects) = filter_for_typesync(enums, tables, objects);
let structs = merge_tables_and_objects(&tables, &objects);
Ok((enums, structs))
}
pub fn generate_all(&self) -> Result<GenerationReport, EvenframeError> {
info!("Starting type generation");
let mut report = GenerationReport::new();
let (enums, tables, objects) = build_all_configs(&self.config)?;
let (enums, tables, objects) = filter_for_typesync(enums, tables, objects);
let registry = ForeignTypeRegistry::from_config(&self.config.foreign_types);
report.enums_processed = enums.len();
report.tables_processed = tables.len();
report.structs_processed = objects.len();
let structs = merge_tables_and_objects(&tables, &objects);
debug!(
"Processing {} enums, {} tables, {} objects",
enums.len(),
tables.len(),
objects.len()
);
fs::create_dir_all(&self.config.output_path)?;
if self.config.arktype {
let file = self.generate_arktype_internal(&structs, &enums, ®istry)?;
report.add_file(file);
}
if self.config.effect {
let file = self.generate_effect_internal(&structs, &enums, ®istry)?;
report.add_file(file);
}
#[cfg(feature = "macroforge")]
if self.config.macroforge {
let file = self.generate_macroforge_internal(&structs, &enums, ®istry)?;
report.add_file(file);
}
#[cfg(feature = "flatbuffers")]
if self.config.flatbuffers {
let file = self.generate_flatbuffers_internal(&structs, &enums, ®istry)?;
report.add_file(file);
}
#[cfg(feature = "protobuf")]
if self.config.protobuf {
let file = self.generate_protobuf_internal(&structs, &enums, ®istry)?;
report.add_file(file);
}
info!(
"Generation complete. Generated {} files",
report.files.len()
);
Ok(report)
}
pub fn generate_arktype(&self) -> Result<GeneratedFile, EvenframeError> {
let (enums, structs) = self.build_typesync_configs()?;
let registry = ForeignTypeRegistry::from_config(&self.config.foreign_types);
fs::create_dir_all(&self.config.output_path)?;
self.generate_arktype_internal(&structs, &enums, ®istry)
}
pub fn generate_effect(&self) -> Result<GeneratedFile, EvenframeError> {
let (enums, structs) = self.build_typesync_configs()?;
let registry = ForeignTypeRegistry::from_config(&self.config.foreign_types);
fs::create_dir_all(&self.config.output_path)?;
self.generate_effect_internal(&structs, &enums, ®istry)
}
#[cfg(feature = "macroforge")]
pub fn generate_macroforge(&self) -> Result<GeneratedFile, EvenframeError> {
let (enums, structs) = self.build_typesync_configs()?;
let registry = ForeignTypeRegistry::from_config(&self.config.foreign_types);
fs::create_dir_all(&self.config.output_path)?;
self.generate_macroforge_internal(&structs, &enums, ®istry)
}
#[cfg(feature = "flatbuffers")]
pub fn generate_flatbuffers(&self) -> Result<GeneratedFile, EvenframeError> {
let (enums, structs) = self.build_typesync_configs()?;
let registry = ForeignTypeRegistry::from_config(&self.config.foreign_types);
fs::create_dir_all(&self.config.output_path)?;
self.generate_flatbuffers_internal(&structs, &enums, ®istry)
}
#[cfg(feature = "protobuf")]
pub fn generate_protobuf(&self) -> Result<GeneratedFile, EvenframeError> {
let (enums, structs) = self.build_typesync_configs()?;
let registry = ForeignTypeRegistry::from_config(&self.config.foreign_types);
fs::create_dir_all(&self.config.output_path)?;
self.generate_protobuf_internal(&structs, &enums, ®istry)
}
fn generate_arktype_internal(
&self,
structs: &HashMap<String, StructConfig>,
enums: &HashMap<String, TaggedUnion>,
registry: &ForeignTypeRegistry,
) -> Result<GeneratedFile, EvenframeError> {
info!("Generating ArkType types");
let content = generate_arktype_type_string(structs, enums, false, registry);
let full_content = format!(
"import {{ scope }} from 'arktype';\n\n{}\n\n export const validator = scope({{\n ...bindings.export(),\n}}).export();",
content
);
let path = self
.config
.output_path
.join(GeneratorType::ArkType.default_filename());
let bytes_written = full_content.len();
fs::write(&path, &full_content)?;
info!("ArkType types written to {:?}", path);
Ok(GeneratedFile {
path,
bytes_written,
generator_type: GeneratorType::ArkType,
})
}
fn generate_effect_internal(
&self,
structs: &HashMap<String, StructConfig>,
enums: &HashMap<String, TaggedUnion>,
registry: &ForeignTypeRegistry,
) -> Result<GeneratedFile, EvenframeError> {
info!("Generating Effect schemas");
let content = generate_effect_schema_string(structs, enums, false, registry);
let full_content = format!("import {{ Schema }} from \"effect\";\n\n{}", content);
let path = self
.config
.output_path
.join(GeneratorType::Effect.default_filename());
let bytes_written = full_content.len();
fs::write(&path, &full_content)?;
info!("Effect schemas written to {:?}", path);
Ok(GeneratedFile {
path,
bytes_written,
generator_type: GeneratorType::Effect,
})
}
#[cfg(feature = "macroforge")]
fn generate_macroforge_internal(
&self,
structs: &HashMap<String, StructConfig>,
enums: &HashMap<String, TaggedUnion>,
registry: &ForeignTypeRegistry,
) -> Result<GeneratedFile, EvenframeError> {
info!("Generating Macroforge types");
let content = generate_macroforge_type_string(
structs,
enums,
false,
self.config.output.array_style,
registry,
);
let path = self
.config
.output_path
.join(GeneratorType::Macroforge.default_filename());
let bytes_written = content.len();
fs::write(&path, &content)?;
info!("Macroforge types written to {:?}", path);
Ok(GeneratedFile {
path,
bytes_written,
generator_type: GeneratorType::Macroforge,
})
}
#[cfg(feature = "flatbuffers")]
fn generate_flatbuffers_internal(
&self,
structs: &HashMap<String, StructConfig>,
enums: &HashMap<String, TaggedUnion>,
registry: &ForeignTypeRegistry,
) -> Result<GeneratedFile, EvenframeError> {
info!("Generating FlatBuffers schema");
let content = generate_flatbuffers_schema_string(
structs,
enums,
self.config.flatbuffers_namespace.as_deref(),
registry,
);
let path = self
.config
.output_path
.join(GeneratorType::FlatBuffers.default_filename());
let bytes_written = content.len();
fs::write(&path, &content)?;
info!("FlatBuffers schema written to {:?}", path);
Ok(GeneratedFile {
path,
bytes_written,
generator_type: GeneratorType::FlatBuffers,
})
}
#[cfg(feature = "protobuf")]
fn generate_protobuf_internal(
&self,
structs: &HashMap<String, StructConfig>,
enums: &HashMap<String, TaggedUnion>,
registry: &ForeignTypeRegistry,
) -> Result<GeneratedFile, EvenframeError> {
info!("Generating Protocol Buffers schema");
let content = generate_protobuf_schema_string(
structs,
enums,
self.config.protobuf_package.as_deref(),
self.config.protobuf_import_validate,
registry,
);
let path = self
.config
.output_path
.join(GeneratorType::Protobuf.default_filename());
let bytes_written = content.len();
fs::write(&path, &content)?;
info!("Protocol Buffers schema written to {:?}", path);
Ok(GeneratedFile {
path,
bytes_written,
generator_type: GeneratorType::Protobuf,
})
}
}