use forge_core::schema::SchemaRegistry;
use crate::Error;
const BUILTIN_TYPES: &[(&str, &str)] = &[(
"TokenPair",
"export interface TokenPair {\n access_token: string;\n refresh_token: string;\n}\n",
)];
pub fn generate(registry: &SchemaRegistry, referenced_types: &[String]) -> Result<String, Error> {
let mut output = String::new();
output.push_str("// Auto-generated by FORGE - DO NOT EDIT\n\n");
let mut tables = registry.all_tables();
tables.sort_by(|a, b| a.struct_name.cmp(&b.struct_name));
let defined_names: std::collections::HashSet<String> =
tables.iter().map(|t| t.struct_name.clone()).collect();
for table in tables {
output.push_str(&format!("export interface {} {{\n", table.struct_name));
for field in &table.fields {
output.push_str(&field.to_typescript());
output.push('\n');
}
output.push_str("}\n\n");
}
for (name, definition) in BUILTIN_TYPES {
if !defined_names.contains(*name) && referenced_types.iter().any(|t| t.as_str() == *name) {
output.push_str(definition);
output.push('\n');
}
}
let mut enums = registry.all_enums();
enums.sort_by(|a, b| a.name.cmp(&b.name));
for enum_def in enums {
output.push_str(&enum_def.to_typescript());
output.push_str("\n\n");
}
output.push_str(
"export type { ForgeError, QueryResult, SubscriptionResult } from \"@forge-rs/svelte\";\n",
);
Ok(output)
}
#[cfg(test)]
mod tests {
use super::*;
use forge_core::schema::{EnumDef, EnumVariant, FieldDef, RustType, TableDef};
#[test]
fn test_generate_with_table() {
let registry = SchemaRegistry::new();
let mut table = TableDef::new("users", "User");
table.fields.push(FieldDef::new("id", RustType::Uuid));
table.fields.push(FieldDef::new("email", RustType::String));
table.fields.push(FieldDef::new(
"avatar_url",
RustType::Option(Box::new(RustType::String)),
));
registry.register_table(table);
let output = generate(®istry, &[]).expect("table types should generate");
assert!(output.contains("export interface User {"));
assert!(output.contains("id: string;"));
assert!(output.contains("email: string;"));
assert!(output.contains("avatar_url?: string;"));
}
#[test]
fn test_generate_with_enum() {
let registry = SchemaRegistry::new();
let mut enum_def = EnumDef::new("ProjectStatus");
enum_def.variants.push(EnumVariant::new("Draft"));
enum_def.variants.push(EnumVariant::new("Active"));
registry.register_enum(enum_def);
let output = generate(®istry, &[]).expect("enum types should generate");
assert!(output.contains("export type ProjectStatus"));
assert!(output.contains("'draft'"));
assert!(output.contains("'active'"));
}
#[test]
fn test_generate_reexports_forge_types() {
let registry = SchemaRegistry::new();
let output = generate(®istry, &[]).expect("type reexports should generate");
assert!(output.contains("ForgeError"));
assert!(output.contains("@forge-rs/svelte"));
}
#[test]
fn test_builtin_token_pair_emitted_when_referenced() {
let registry = SchemaRegistry::new();
let refs = vec!["TokenPair".to_string()];
let output = generate(®istry, &refs).expect("builtin types should generate");
assert!(output.contains("export interface TokenPair {"));
assert!(output.contains("access_token: string;"));
assert!(output.contains("refresh_token: string;"));
}
#[test]
fn test_builtin_not_emitted_when_unreferenced() {
let registry = SchemaRegistry::new();
let output = generate(®istry, &[]).expect("no builtins when unreferenced");
assert!(!output.contains("TokenPair"));
}
}