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