mod emit;
use std::path::Path;
use crate::cli::ConnectorSchemaCodegenArgs;
const DEFAULT_OUTPUT: &str = "crates/harn-vm/src/triggers/event/schemas_generated.rs";
pub(crate) fn run(args: &ConnectorSchemaCodegenArgs) -> i32 {
match run_inner(args) {
Ok(code) => code,
Err(message) => {
eprintln!("harn connector-schema-codegen: {message}");
1
}
}
}
fn run_inner(args: &ConnectorSchemaCodegenArgs) -> Result<i32, String> {
let source = schema_source(args.schema.as_deref())?;
let header = header_for();
let rendered = emit::render(&source, &header)?;
let out = Path::new(args.out.as_deref().unwrap_or(DEFAULT_OUTPUT));
if args.check {
return Ok(check_against(out, &rendered));
}
harn_vm::atomic_io::atomic_write(out, rendered.as_bytes())
.map_err(|error| format!("failed to write {}: {error}", out.display()))?;
eprintln!("wrote {}", out.display());
Ok(0)
}
fn schema_source(schema_path: Option<&str>) -> Result<String, String> {
match schema_path {
Some(path) => std::fs::read_to_string(path)
.map_err(|error| format!("failed to read schema module {path}: {error}")),
None => Ok(harn_stdlib::CONNECTOR_EVENT_SCHEMAS_SOURCE.to_string()),
}
}
fn check_against(out: &Path, rendered: &str) -> i32 {
match std::fs::read_to_string(out) {
Ok(existing) if normalize(&existing) == normalize(rendered) => 0,
Ok(_) => {
eprintln!(
"{} is out of date; regenerate with `make gen-connector-schemas`",
out.display()
);
1
}
Err(_) => {
eprintln!(
"{} is missing; generate it with `make gen-connector-schemas`",
out.display()
);
1
}
}
}
fn normalize(text: &str) -> String {
text.replace("\r\n", "\n")
}
fn header_for() -> String {
HEADER.to_string()
}
const HEADER: &str = "\
// DO NOT EDIT — generated by `harn connector-schema-codegen`.
//
// Source of truth: crates/harn-stdlib/src/stdlib/stdlib_event_schemas.harn
// Regenerate with: make gen-connector-schemas
// Verify (CI): make check-connector-schemas
//
// These structs are generated from canonical Harn `type` declarations so a
// connector's normalized-event JSON matches the Rust struct by construction.
// This file currently coexists with the hand-written `GitHubEventPayload`
// family in `payloads.rs`; switching the trigger boundary to produce these
// typed payloads is a separate follow-up.
";
#[cfg(test)]
mod tests;