Skip to main content
Verticopolis engineGitHub

verticopolis_engine/catalog/
json.rs

1// The catalog as JSON: the value, the canonical text and its hash.
2
3use serde_json::Value;
4use std::sync::OnceLock;
5
6use super::{catalog, lock_view};
7use crate::canonical::{canonical_json, digest};
8use crate::clock::GameMode;
9
10// The catalog for `mode` as a JSON value, built once per mode: a boot reads
11// both catalogs through the binding and hashes both for the engine's
12// identity, and the catalog depends on nothing but the mode.
13fn shared_catalog_value(mode: GameMode) -> &'static Value {
14    static CLASSIC: OnceLock<Value> = OnceLock::new();
15    static MODERN: OnceLock<Value> = OnceLock::new();
16    let cell = match mode {
17        GameMode::Classic => &CLASSIC,
18        GameMode::Modern => &MODERN,
19    };
20    cell.get_or_init(|| serde_json::to_value(catalog(mode)).expect("the catalog serializes"))
21}
22
23/// The catalog for `mode` as a JSON value.
24pub fn catalog_value(mode: GameMode) -> Value {
25    shared_catalog_value(mode).clone()
26}
27
28/// The catalog for `mode` as canonical JSON text: the bytes the lock hashes,
29/// and the text the JavaScript package returns.
30pub fn catalog_json(mode: GameMode) -> String {
31    canonical_json(shared_catalog_value(mode))
32}
33
34/// The canonical hash of the catalog for `mode`, as
35/// `conformance/catalog-digests.json` records it: the lock view, so the
36/// exempt footprints do not move it.
37pub fn catalog_digest(mode: GameMode) -> String {
38    digest(&lock_view(shared_catalog_value(mode)))
39}