pushkin-compiler 0.2.1

Schema compiler for pushkin: canonical JSON Schema emission and generated bindings
Documentation
//! Rust emission via `typify` (the R5/R6-approved adoption, Phase 6
//! task 2). The constrained-subset parser remains the loud front gate:
//! typify only ever receives a schema `schema::parse` already accepted.
//! Strictness survives the swap (`additionalProperties: false` →
//! `deny_unknown_fields`) and improves: length and pattern constraints
//! are now enforced at construction/deserialization by the generated
//! code. Determinism comes from the exact-pinned typify + prettyplease.

use typify::{TypeSpace, TypeSpaceSettings};

use crate::schema::ContractSchema;
use crate::targets::{header, type_name};
use crate::CompileError;

pub struct EmitInput<'a> {
    pub schema: &'a ContractSchema,
    pub raw_schema_json: &'a str,
    pub epoch: u32,
}

/// # Errors
/// `CompileError::InvalidSchema` if the (already subset-validated) schema
/// fails typify's stricter structural handling — loud, never silent.
pub fn emit(input: &EmitInput<'_>) -> Result<String, CompileError> {
    let body = typify_body(input)?;
    // Header FIRST: the daemon's epoch probe reads the first
    // `pushkin-epoch:` marker, and typify doc comments may embed the
    // canonical schema's own $comment.
    Ok(format!(
        "{header}{body}",
        header = header(input.schema, input.epoch, "//"),
    ))
}

fn typify_body(input: &EmitInput<'_>) -> Result<String, CompileError> {
    let mut value: serde_json::Value =
        serde_json::from_str(input.raw_schema_json).map_err(|error| invalid(&error))?;
    // typify names the root type from `title`; inject the deterministic
    // contract type name (the canonical artifact itself is unchanged).
    value["title"] = serde_json::Value::String(type_name(input.schema));
    let root: schemars::schema::RootSchema =
        serde_json::from_value(value).map_err(|error| invalid(&error))?;

    let settings = TypeSpaceSettings::default();
    let mut space = TypeSpace::new(&settings);
    space
        .add_root_schema(root)
        .map_err(|error| invalid(&error))?;
    let file = syn::parse2::<syn::File>(space.to_stream()).map_err(|error| invalid(&error))?;
    Ok(prettyplease::unparse(&file))
}

fn invalid(error: &dyn std::fmt::Display) -> CompileError {
    CompileError::InvalidSchema {
        message: format!("typify Rust generation rejected the schema: {error}"),
    }
}