Skip to main content

nmbrs_workload/
catalog.rs

1// Copyright 2024-2026 Jonathan Shook
2// SPDX-License-Identifier: Apache-2.0
3
4//! Bundled-workload catalog — SRD-85.
5//!
6//! Workloads embedded into the artifact, discoverable by
7//! catalog name (`cql/keyvalue`, `examples/lfsr`). The catalog
8//! is assembled once at process startup from the manifests each
9//! contributing crate generates at build time (see
10//! `tools/bundle-gen`): the core binary contributes the curated
11//! `workloads/` set and the `examples/` tier; adapter crates
12//! contribute their own `adapters/<a>/workloads/` sets behind
13//! their feature gates, so the catalog is truthful about what
14//! *this* binary can run.
15//!
16//! Visibility tiers separate the two audiences: everything in
17//! the catalog is runnable by name, but only the
18//! [`Tier::Curated`] entries are *listed* by default —
19//! `nmbrs describe workloads` shows the products, not fifty test
20//! fixtures; `--all` (or the `examples` subtopic) reveals the
21//! rest.
22//!
23//! Resolution policy lives with the resolver
24//! (`nmbrs-runtime::runner::resolve_workload`): nearest-first —
25//! the logical filesystem location is favored over the catalog
26//! by default, and a name that resolves both ways picks the
27//! local file with a LOGGED WARNING naming both candidates.
28//! Shadowing is allowed but never silent; `--strict` promotes
29//! the warning to an error.
30
31use std::sync::OnceLock;
32
33/// Visibility tier of a bundled workload.
34#[derive(Debug, Clone, Copy, PartialEq, Eq)]
35pub enum Tier {
36    /// Curated, real-world workloads — listed by default.
37    /// Required to carry a `description:` (lint-enforced).
38    Curated,
39    /// Teaching examples and coverage workloads — bundled and
40    /// runnable (artifact smoke-testing as-is, anywhere), but
41    /// unlisted unless explicitly requested.
42    Example,
43}
44
45impl Tier {
46    pub fn as_str(self) -> &'static str {
47        match self {
48            Tier::Curated => "curated",
49            Tier::Example => "example",
50        }
51    }
52}
53
54/// One embedded workload.
55#[derive(Debug)]
56pub struct BundledWorkload {
57    /// Catalog name: `<namespace>/<stem>` (no extension), e.g.
58    /// `cql/keyvalue`, `examples/signals/lfsr`. Top-level
59    /// curated entries have no namespace.
60    pub name: &'static str,
61    /// Visibility tier.
62    pub tier: Tier,
63    /// The yaml source, embedded at build time.
64    pub source: &'static str,
65}
66
67static CATALOG: OnceLock<Vec<&'static BundledWorkload>> = OnceLock::new();
68
69/// Install the catalog from the contributing manifests. Called
70/// once at process startup (before any workload resolution);
71/// subsequent calls are ignored — the first installation wins,
72/// which keeps test harnesses that re-enter startup idempotent.
73///
74/// Panics on a duplicate catalog name across sets: two crates
75/// claiming one name is a build/packaging bug, not an operator
76/// condition.
77pub fn install(sets: &[&'static [BundledWorkload]]) {
78    let _ = CATALOG.set({
79        let mut all: Vec<&'static BundledWorkload> = sets.iter().flat_map(|s| s.iter()).collect();
80        all.sort_by_key(|w| w.name);
81        for pair in all.windows(2) {
82            assert_ne!(
83                pair[0].name, pair[1].name,
84                "bundled workload name collision across catalog sets: `{}`",
85                pair[0].name,
86            );
87        }
88        all
89    });
90}
91
92/// Exact-name lookup. No globbing, no fuzzy matching —
93/// `nmbrs describe workloads` is the discovery surface, not the
94/// resolver.
95pub fn lookup(name: &str) -> Option<&'static BundledWorkload> {
96    CATALOG
97        .get()?
98        .binary_search_by_key(&name, |w| w.name)
99        .ok()
100        .map(|idx| CATALOG.get().unwrap()[idx])
101}
102
103/// All catalog entries in name order. Empty when no catalog was
104/// installed (library consumers without the nmbrs binary's
105/// startup hook).
106pub fn iter() -> impl Iterator<Item = &'static BundledWorkload> {
107    CATALOG
108        .get()
109        .map(|v| v.as_slice())
110        .unwrap_or(&[])
111        .iter()
112        .copied()
113}
114
115/// Entries of one tier, in name order.
116pub fn iter_tier(tier: Tier) -> impl Iterator<Item = &'static BundledWorkload> {
117    iter().filter(move |w| w.tier == tier)
118}
119
120#[cfg(test)]
121mod tests {
122    use super::*;
123
124    // The install-once global makes classic unit isolation
125    // awkward; these tests exercise the pure parts and a single
126    // install path. (E2e coverage drives the real assembled
127    // catalog through the nmbrs binary.)
128    static SET_A: &[BundledWorkload] = &[
129        BundledWorkload {
130            name: "alpha",
131            tier: Tier::Curated,
132            source: "description: a\n",
133        },
134        BundledWorkload {
135            name: "examples/beta",
136            tier: Tier::Example,
137            source: "# b\n",
138        },
139    ];
140
141    #[test]
142    fn install_lookup_and_tier_filter() {
143        install(&[SET_A]);
144        // Idempotent re-install is a no-op.
145        install(&[]);
146        assert!(lookup("alpha").is_some());
147        assert!(lookup("examples/beta").is_some());
148        assert!(lookup("nope").is_none());
149        let curated: Vec<_> = iter_tier(Tier::Curated).map(|w| w.name).collect();
150        assert_eq!(curated, vec!["alpha"]);
151        let all: Vec<_> = iter().map(|w| w.name).collect();
152        assert_eq!(all, vec!["alpha", "examples/beta"]);
153    }
154}