use std::fs;
use anyhow::{Context, Result};
use fraiseql_core::schema::{CompiledSchema, generate_capture_trigger_ddl};
use fraiseql_observers::migrations::entity_change_log_capture_trigger_sql;
use crate::output::OutputFormatter;
#[cfg(test)]
mod tests;
#[derive(Debug, Clone)]
pub struct GenerateCaptureTriggersConfig {
pub schema_path: String,
pub output: Option<String>,
pub include_function: bool,
}
#[must_use]
pub fn build_ddl(schema: &CompiledSchema, include_function: bool) -> String {
let triggers = generate_capture_trigger_ddl(schema);
if triggers.is_empty() {
return String::new();
}
let mut out = String::new();
if include_function {
out.push_str(entity_change_log_capture_trigger_sql());
out.push('\n');
}
out.push_str(&triggers);
out
}
pub fn run(config: GenerateCaptureTriggersConfig, formatter: &OutputFormatter) -> Result<()> {
let schema_json = fs::read_to_string(&config.schema_path)
.with_context(|| format!("Failed to read schema '{}'", config.schema_path))?;
let schema = CompiledSchema::from_json(&schema_json, false)
.context("Failed to parse compiled schema")?;
let ddl = build_ddl(&schema, config.include_function);
if ddl.is_empty() {
formatter.progress(
"No @subscribable types in the schema — no capture triggers to generate. \
Declare one with @fraiseql.type(subscribable_tables=[\"tb_post\"]).",
);
return Ok(());
}
let trigger_count = schema.subscribable.iter().map(|e| e.tables.len() * 3).sum::<usize>();
match &config.output {
Some(path) => {
fs::write(path, &ddl)
.with_context(|| format!("Failed to write output file '{path}'"))?;
formatter.progress(&format!(
"✓ Wrote {trigger_count} capture trigger(s) for {} subscribable type(s) to {path}",
schema.subscribable.len()
));
},
None => print!("{ddl}"),
}
Ok(())
}