byteor_pipeline_exec/registry.rs
1//! Stage registry helpers.
2//!
3//! These helpers are intended for ergonomic stage resolution at startup.
4//!
5//! Design goals:
6//! - zero cost in hot loops: stages resolve once, then executors run specialized loops
7//! - small surface: avoid turning stage registration into a DSL
8//! - cold-path linear search is acceptable (resolution is not per-message)
9
10use crate::{ResolvedStage, StageResolver};
11
12/// Factory function for producing a [`ResolvedStage`].
13///
14/// Notes:
15/// - called during role startup (cold path)
16/// - may allocate if returning [`ResolvedStage::Stateful`] or [`ResolvedStage::StatefulTransform`]
17pub type ResolvedStageFactory = fn() -> ResolvedStage;
18
19/// One named stage entry for use with [`SliceStageRegistry`].
20#[derive(Clone, Copy)]
21pub struct StageEntry {
22 /// Stable stage name.
23 pub name: &'static str,
24 /// Factory for the resolved stage callable.
25 pub factory: ResolvedStageFactory,
26}
27
28/// A minimal slice-based stage registry.
29///
30/// This is the intended default for simple runtimes: define a `static` slice of stage entries and
31/// resolve by name.
32///
33/// ```
34/// use byteor_pipeline_exec::{ResolvedStage, SliceStageRegistry, StageEntry, StageResolver};
35///
36/// fn identity() -> ResolvedStage {
37/// ResolvedStage::MapOk(|input, output| {
38/// let n = input.len().min(output.len());
39/// output[..n].copy_from_slice(&input[..n]);
40/// n
41/// })
42/// }
43///
44/// static STAGES: &[StageEntry] = &[StageEntry { name: "identity", factory: identity }];
45///
46/// let reg = SliceStageRegistry::new(STAGES);
47/// assert!(reg.resolve("identity").is_some());
48/// ```
49pub struct SliceStageRegistry {
50 entries: &'static [StageEntry],
51}
52
53impl SliceStageRegistry {
54 /// Create a registry from a slice of stage entries.
55 pub const fn new(entries: &'static [StageEntry]) -> Self {
56 Self { entries }
57 }
58
59 /// Access the underlying entries.
60 pub fn entries(&self) -> &'static [StageEntry] {
61 self.entries
62 }
63}
64
65impl StageResolver for SliceStageRegistry {
66 fn resolve(&self, stage: &str) -> Option<ResolvedStage> {
67 self.entries
68 .iter()
69 .find(|e| e.name == stage)
70 .map(|e| (e.factory)())
71 }
72}