dig-rpc-protocol 0.7.0

Canonical DIG-node JSON-RPC protocol: request/response types, the method enum + tier classification, the error-code taxonomy, and an OpenRPC 1.2.6 document generator. The single source of truth both DIG node implementations depend on. Pure types — no I/O, no async, no server logic. (Formerly dig-rpc-types.)
Documentation
//! Band invariants for [`ErrorCode`]: a new code must not collide with a numeric
//! range this ecosystem has already published.
//!
//! An error code is a permanent, published branch key — a consumer switches on it
//! to decide whether to retry a holder or abandon it. A new variant landing inside
//! a band another subsystem already published is therefore not a cosmetic defect:
//! it makes two different failures indistinguishable at exactly the moment the
//! client must tell them apart.
//!
//! Plain numeric uniqueness across the whole enum is already asserted by
//! `error::tests::all_codes_unique_and_complete` in the crate itself; this file
//! covers what that test cannot see — the published BAND a number falls in.

use dig_rpc_protocol::error::ErrorCode;

/// The onion / private-retrieval band, published normative in `SYSTEM.md` →
/// "Canonical DIG-node RPC interface". `-32021` in particular is
/// `PRIVACY_REQUIRES_LOCAL_NODE` and MUST NOT be reused.
const ONION_BAND: std::ops::RangeInclusive<i32> = -32022..=-32020;

/// **Proves:** `RANGE_METADATA_UNREPRESENTABLE` exists, sits outside the onion
/// band, and is not `-32021`.
///
/// It is load-bearing rather than decorative: dig-nat's encoder directs a holder
/// to answer this code when a resource's metadata alone cannot fit a frame — a
/// permanent property of that holder's view of the resource. A client that cannot
/// tell it from an ordinary transport failure will retry a holder that can never
/// succeed, which is why it needs its own number rather than a shared one.
#[test]
fn range_metadata_unrepresentable_is_outside_the_onion_band() {
    let code = ErrorCode::RangeMetadataUnrepresentable.code();

    assert_ne!(code, -32021, "-32021 is PRIVACY_REQUIRES_LOCAL_NODE");
    assert!(
        !ONION_BAND.contains(&code),
        "code {code} falls inside the published onion band {ONION_BAND:?}"
    );
    assert_eq!(
        ErrorCode::RangeMetadataUnrepresentable.machine_code(),
        "RANGE_METADATA_UNREPRESENTABLE"
    );
    assert!(
        ErrorCode::RangeMetadataUnrepresentable.is_jsonrpc_reserved(),
        "the code must live in the JSON-RPC implementation-defined server band"
    );
}

/// **Proves:** the module-level rustdoc taxonomy table lists EVERY code in
/// [`ErrorCode::ALL`], with the same number and variant name.
///
/// That table is the taxonomy published on docs.rs, and it is where a reimplementer
/// builds its branch table from — so a code missing there is a code that
/// reimplementation treats as a generic server error, which for
/// `RANGE_METADATA_UNREPRESENTABLE` means retrying a holder that can never serve the
/// range. The table was hand-maintained beside the enum and had already drifted once
/// by exactly one row, so the agreement is asserted mechanically rather than trusted.
#[test]
fn the_rustdoc_taxonomy_table_lists_every_code() {
    // The source of the module whose doc comment IS the published table. Read at
    // compile time so this test cannot go stale against a moved file.
    const ERROR_MODULE_SOURCE: &str = include_str!("../src/error.rs");

    let table: String = ERROR_MODULE_SOURCE
        .lines()
        .take_while(|line| line.starts_with("//!"))
        .collect::<Vec<_>>()
        .join("\n");

    for code in ErrorCode::ALL {
        let row = format!("| `{}` | [`{:?}`]", code.code(), code);
        assert!(
            table.contains(&row),
            "the rustdoc taxonomy table is missing a row for {} ({}); expected to find {row:?}",
            code.machine_code(),
            code.code()
        );
    }
}

/// **Proves:** the new code is reachable through the OpenRPC error catalogue, so a
/// peer that discovers the surface learns it can be answered with this code.
///
/// dig-node's serve leg answers it and dig-download's client branches on it; both
/// discover the taxonomy from this document rather than from Rust.
#[test]
fn range_metadata_unrepresentable_is_published_in_openrpc() {
    let doc = dig_rpc_protocol::openrpc::openrpc_document("0.0.0-test");
    let catalogue = doc["components"]["x-dig-errors"]
        .as_array()
        .expect("error catalogue is an array");

    let entry = catalogue
        .iter()
        .find(|e| e["machineCode"] == "RANGE_METADATA_UNREPRESENTABLE")
        .expect("RANGE_METADATA_UNREPRESENTABLE is absent from the OpenRPC catalogue");

    assert_eq!(
        entry["code"],
        ErrorCode::RangeMetadataUnrepresentable.code(),
        "the published number must match the enum"
    );
}