Gunnar

nordisk/monetize

crates/monetize-proto/build.rs

//! Compiles `proto/monetize/v1/*.proto` into `$OUT_DIR/monetize.v1.rs`.
//!
//! Deliberately protoc-free (pure Rust, nothing shelled out to): `protox` parses the
//! `.proto` sources and hands `tonic-prost-build` a ready `FileDescriptorSet` —
//! the shape `gunnar-ui-server/build.rs` uses, copied. `protox`'s `prost-types`
//! major must equal `tonic-prost-build`'s or `compile_fds` takes a different
//! `FileDescriptorSet` type; the workspace pins both at 0.14 / protox 0.9.
//!
//! Both halves are built: monetize-server implements the server, the console BFF
//! and the CLI consume the client.

use std::path::PathBuf;

const PROTOS: &[&str] = &[
    "common.proto",
    "entitlement.proto",
    "purchase.proto",
    "usage.proto",
    "products.proto",
    "ledger.proto",
    "events.proto",
    // The paid-signup seam: the return page's one poll, and the operator's list.
    "signup.proto",
    "signup_admin.proto",
    // The TAX RETURN seam: the periods a year is filed in, and one filled form.
    "tax.proto",
    // The refused vendor callbacks: the operator's read of the bounded ring a
    // failed delivery now leaves a row in.
    "callback_admin.proto",
    // What the VENDOR says it took and monetize did not fulfil: the
    // reconciler's durable rows, the operator's read of them.
    "reconcile_admin.proto",
    // The data-set growth: the OUTSIDE actor's console — status, quote,
    // approve, resume (`DATA-SET-GROWTH-FLOW.md` T6).
    "grow_admin.proto",
    // The estate, drawn: one board, one chip per machine. The picture crosses
    // the wire because only monetize-server holds both halves it is drawn from.
    "estate.proto",
    // A private gunnar, ordered: the order page's two calls over the
    // lay-estate job (`monetize_server::estate_order`).
    "estate_order.proto",
    // ONE monetize: every (product, tenant) account and the rows on it — the
    // subscription, the purchases, the private instances it owns.
    "accounts.proto",
    // The pull channel (5.4): estates report, pull signed orders and ask; and
    // the guardian's console (cases, the buttons, the auto-fix opt-in).
    "fleet.proto",
    // The operator's TEST BUTTON (2026-10-11): each product's declared test
    // actions, run through the same sign-up / estate-order path a customer takes.
    "test_purchase.proto",
    // The operator's SITE BOOK (2026-10-14): a site added and edited in the application,
    // judged by the box's own loader, applied live, audited.
    "site_book.proto",
    // The ONE box's PLUGINS (2026-10-17): the release's signed plugin list taken from the
    // release bucket, verified before a byte is written, swapped live by the guest watcher.
    "plugins.proto",
    // The ESTATES that exist and the NEW-ESTATE slot (2026-10-29, lane OVW-CLEAN): the console's
    // Overview, classified by the server; a new estate handed to the one site order.
    "estates.proto",
];

fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Read when the script RUNS, not when it was compiled: the target dir is
    // shared across checkouts, and a script compiled in another one kept that
    // checkout's path baked in and failed here with "not in any include path".
    let include = PathBuf::from(std::env::var("CARGO_MANIFEST_DIR")?).join("proto");
    let files: Vec<PathBuf> = PROTOS.iter().map(|f| include.join("monetize/v1").join(f)).collect();
    for file in &files {
        println!("cargo:rerun-if-changed={}", file.display());
    }
    let descriptors = protox::compile(&files, [&include])?;
    tonic_prost_build::configure()
        .build_server(true)
        .build_client(true)
        // `default-stubs` (test fakes only): every server method gets tonic's UNIMPLEMENTED
        // body, so a fake writes only what its test calls. A real server never names it.
        .generate_default_stubs(std::env::var_os("CARGO_FEATURE_DEFAULT_STUBS").is_some())
        // Every `map<>` as a BTreeMap: the Rust contracts use BTreeMap, so the caps
        // convert without a copy and serialize in one stable order.
        .btree_map(".")
        // Nothing on this path needs protoc; a stray PROTOC in the environment must
        // not change the outcome.
        .skip_protoc_run()
        .compile_fds(descriptors)?;
    Ok(())
}