monty-proto 0.0.21

A sandboxed, snapshotable Python interpreter written in Rust.
Documentation
//! Regenerates the checked-in prost code from the `.proto` schema.
//!
//! Run via `make generate-proto`. Uses `protox` (a pure-Rust protobuf
//! compiler) so no `protoc` binary is required. Two outputs, both checked in
//! and both diffed in CI by `make check-proto` so the schema and the
//! generated code can never drift:
//!
//! - `src/generated/monty.v1.rs` — the protocol messages, with the
//!   `monty.v1.MontyObject` message mapped via `extern_path` onto the
//!   hand-written [`WireObject`](../wire.rs) so values encode/decode straight
//!   to `monty_types::MontyObject` with no mirror struct.
//! - `tests/oracle/monty.v1.rs` — the same schema *without* the mapping: a
//!   fully prost-generated mirror used only by `tests/differential.rs` to
//!   prove the hand-written implementation is byte-compatible with prost.

use std::{fs, path::Path};

/// Header prepended to the generated files: marks them as generated and
/// disables the workspace lints, which generated code cannot be expected to
/// satisfy. `allow` (not `expect`) is deliberate — which lints fire depends
/// on the prost-build version, so expectations would themselves churn.
const HEADER: &str = "\
// @generated by `make generate-proto` from proto/monty/v1/monty.proto — DO NOT EDIT.
#![allow(clippy::allow_attributes, clippy::pedantic, clippy::use_self, clippy::absolute_paths, missing_docs)]
";

/// Header for the test-only oracle: the differential tests exercise only the
/// value messages, so the rest of the schema is (expected) dead code.
const ORACLE_HEADER: &str = "\
// @generated by `make generate-proto` from proto/monty/v1/monty.proto — DO NOT EDIT.
#![allow(clippy::allow_attributes, clippy::pedantic, clippy::use_self, clippy::absolute_paths, missing_docs, dead_code)]
";

fn main() {
    let manifest_dir = Path::new(env!("CARGO_MANIFEST_DIR"));
    let proto_dir = manifest_dir.join("proto");
    let proto_file = proto_dir.join("monty/v1/monty.proto");

    let descriptors = protox::compile([&proto_file], [&proto_dir]).expect("failed to compile monty.proto");

    // protocol messages: MontyObject is the hand-written WireObject
    let out_dir = manifest_dir.join("src/generated");
    prost_build::Config::new()
        .out_dir(&out_dir)
        .extern_path(".monty.v1.MontyObject", "crate::WireObject")
        .extern_path(".monty.v1.FunctionCall", "crate::WireFunctionCall")
        .compile_fds(descriptors.clone())
        .expect("failed to generate Rust code from monty.proto");
    prepend_header(&out_dir.join("monty.v1.rs"), HEADER);

    // differential-test oracle: the same schema, fully generated
    let oracle_dir = manifest_dir.join("tests/oracle");
    fs::create_dir_all(&oracle_dir).expect("failed to create tests/oracle");
    prost_build::Config::new()
        .out_dir(&oracle_dir)
        .compile_fds(descriptors)
        .expect("failed to generate oracle Rust code from monty.proto");
    prepend_header(&oracle_dir.join("monty.v1.rs"), ORACLE_HEADER);
}

/// Prepends `header` to a freshly generated file and reports it.
fn prepend_header(generated: &Path, header: &str) {
    let body = fs::read_to_string(generated).expect("generated file missing");
    fs::write(generated, format!("{header}{body}")).expect("failed to write generated file");
    println!("regenerated {}", generated.display());
}