pushkin-compiler 0.2.0

Schema compiler for pushkin: canonical JSON Schema emission and generated bindings
Documentation
//! Pushkin compiler: canonical JSON Schema 2020-12 → strict
//! four-target bindings (TS/Zod, Pydantic v2, Rust serde, SQL DDL), each
//! stamped with a schema-epoch header (spec §5.2).
//!
//! Crate contract: emission is deterministic (same input → byte-identical
//! output) and total over the constrained authorable subset; anything
//! outside the subset is a loud `Unrepresentable` rejection with named
//! alternatives — never a silent drop (spec §5.1).

mod schema;
mod targets;

use serde::Deserialize;
use thiserror::Error;

pub use schema::{ContractSchema, Property, PropertyKind};

#[derive(Debug, Error)]
pub enum CompileError {
    #[error("contract schema is not valid JSON Schema 2020-12: {message}")]
    InvalidSchema { message: String },
    #[error(
        "construct '{construct}' does not round-trip to all targets; \
         use one of the supported alternatives: {alternatives}"
    )]
    Unrepresentable {
        construct: String,
        alternatives: String,
    },
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum Target {
    Zod,
    Pydantic,
    Rust,
    Sql,
}

#[derive(Debug)]
pub struct CompileRequest {
    pub contract_name: String,
    pub schema_json: String,
    pub target: Target,
    pub epoch: u32,
}

#[derive(Debug)]
pub struct CompiledBinding {
    pub content: String,
}

/// Compiles one contract schema to one target binding.
///
/// # Errors
/// Returns `CompileError::InvalidSchema` for malformed input and
/// `CompileError::Unrepresentable` (with alternatives) for constructs
/// outside the constrained authorable subset.
pub fn compile(request: &CompileRequest) -> Result<CompiledBinding, CompileError> {
    let schema = schema::parse(&request.contract_name, &request.schema_json)?;
    let content = match request.target {
        Target::Zod => targets::zod::emit(&schema, request.epoch),
        Target::Pydantic => targets::pydantic::emit(&schema, request.epoch),
        Target::Rust => targets::rust::emit(&targets::rust::EmitInput {
            schema: &schema,
            raw_schema_json: &request.schema_json,
            epoch: request.epoch,
        })?,
        Target::Sql => targets::sql::emit(&schema, request.epoch),
    };
    Ok(CompiledBinding { content })
}