use anyhow::{bail, Context, Result};
use super::external;
const EMIT_SCRIPT: &str = r#"
const sourcePath = process.env.PUSHKIN_SOURCE;
const exportName = process.env.PUSHKIN_EXPORT;
const epoch = process.env.PUSHKIN_EPOCH;
const module = await import(sourcePath);
const schema = module[exportName];
if (schema === undefined) {
console.error(`pushkin: export '${exportName}' not found in ${sourcePath}`);
process.exit(3);
}
const { z } = await import("zod");
const jsonSchema = z.toJSONSchema(schema, { target: "draft-2020-12", io: "input" });
const withHeader = {
$schema: "https://json-schema.org/draft/2020-12/schema",
$comment: `GENERATED by pushkin compile from ${sourcePath.split("/").pop()} — do not hand-edit. pushkin-epoch: ${epoch}`,
...jsonSchema,
};
console.log(JSON.stringify(withHeader, null, 2));
"#;
pub fn emit_schema_from_zod(source: &str, export_name: &str, epoch: u32) -> Result<String> {
let absolute = std::fs::canonicalize(source)
.with_context(|| format!("contract source '{source}' not found"))?;
let output = external::bun_emit(
EMIT_SCRIPT,
&[
("PUSHKIN_SOURCE", &absolute.to_string_lossy()),
("PUSHKIN_EXPORT", export_name),
("PUSHKIN_EPOCH", &epoch.to_string()),
],
)
.with_context(|| format!("Zod authoring emission failed for '{source}'"))?;
if !output.status.success() {
bail!(
"Zod authoring emission failed for '{source}' (export {export_name}): {}",
String::from_utf8_lossy(&output.stderr).trim()
);
}
let mut schema = String::from_utf8(output.stdout).context("emitted schema is not UTF-8")?;
if !schema.ends_with('\n') {
schema.push('\n');
}
Ok(schema)
}
pub fn export_name_for(contract: &str) -> String {
let mut pascal = String::new();
for part in contract.split(['-', '_']) {
let mut chars = part.chars();
if let Some(first) = chars.next() {
pascal.extend(first.to_uppercase());
pascal.push_str(chars.as_str());
}
}
format!("{pascal}CreateSchema")
}