Skip to main content

soma_som_ring/
lib.rs

1// SPDX-License-Identifier: LGPL-3.0-only
2#![warn(missing_docs)]
3#![cfg_attr(test, allow(clippy::unwrap_used, clippy::expect_used, clippy::panic))]
4#![doc = include_str!("../README.md")]
5
6//! `soma-som-ring` — the standalone ring execution engine for [soma(som)].
7//!
8//! This crate owns the cycle lifecycle (genesis + standard cycles) and the
9//! registration API for the five extension relationships (BEFORE / THROUGH /
10//! AFTER / AROUND / WITHIN) defined in OPUS §3.
11//!
12//! [soma(som)]: https://somasom.io
13//!
14//! ## What lives here
15//!
16//! | Component | Module |
17//! |---|---|
18//! | `RingEngine` | [`engine`] |
19//! | `RingProcessor` trait | [`processor`] |
20//! | `CycleObserver`, `NoOpObserver` | [`engine`] |
21//! | `GenesisReport`, `CycleReport` | [`engine`] |
22//! | `RingEngineError` | [`error`] |
23//! | `RingCommand`, `ViewIntent`, `CommandDispatcher` | [`command`], [`dispatch`] |
24//! | `FederationBridge` | [`federation`] |
25//! | `Boundary` (crossing attestation) | [`boundary`] |
26//!
27//! Structural primitives (`Quad`, `Tree`, `Ring`, `UnitId`, the extension
28//! trait family, persistence abstractions, governance trait interfaces) live
29//! in the foundation crate [`soma_som_core`]. The split is the layer-purity
30//! boundary: foundation primitives below, execution mechanics above.
31//!
32//! ## Dependencies
33//!
34//! ```text
35//! soma-som-core ◄─── soma-som-ring
36//! ```
37//!
38//! Zero framework dependencies: no async runtime, no HTTP server, no libc
39//! bindings. Signing is injected at construction via the
40//! [`soma_som_core::CrossingSigner`] trait so the engine never names a
41//! concrete crypto backend.
42//!
43//! ## Example
44//!
45//! Construct a `RingEngine`, register a passthrough processor for each unit,
46//! and run a single genesis cycle:
47//!
48//! ```
49//! # fn main() -> Result<(), soma_som_ring::RingEngineError> {
50//! use soma_som_core::TimingConfig;
51//! use soma_som_core::quad::Quad;
52//! use soma_som_core::types::{Layer, UnitId};
53//! use soma_som_ring::{RingEngine, RingEngineError, RingProcessor};
54//!
55//! // Minimal RingProcessor — passes input through unchanged.
56//! struct Echo;
57//! impl RingProcessor for Echo {
58//!     fn process(
59//!         &mut self,
60//!         _unit: UnitId,
61//!         _cycle: u64,
62//!         input: &Quad,
63//!         _data: &Quad,
64//!     ) -> Result<Quad, RingEngineError> {
65//!         Ok(input.clone())
66//!     }
67//! }
68//!
69//! let mut engine = RingEngine::new(TimingConfig::default());
70//! engine.set_all_processors(|| Box::new(Echo));
71//! assert_eq!(engine.active_units().len(), 6);
72//!
73//! // Genesis: bootstrap the ring with a seed.
74//! let report = engine.genesis(b"example-seed")?;
75//! assert!(engine.is_genesis_complete());
76//! assert_eq!(report.crossing_records.len(), 12);
77//! # Ok(())
78//! # }
79//! ```
80//!
81//! After genesis, drive standard cycles (each cycle traverses all 6 units +
82//! the 12 crossings of the ring topology):
83//!
84//! ```
85//! # use soma_som_core::TimingConfig;
86//! # use soma_som_core::quad::Quad;
87//! # use soma_som_core::types::UnitId;
88//! # use soma_som_ring::{RingEngine, RingEngineError, RingProcessor};
89//! # struct Echo;
90//! # impl RingProcessor for Echo {
91//! #     fn process(
92//! #         &mut self,
93//! #         _u: UnitId,
94//! #         _c: u64,
95//! #         input: &Quad,
96//! #         _d: &Quad,
97//! #     ) -> Result<Quad, RingEngineError> {
98//! #         Ok(input.clone())
99//! #     }
100//! # }
101//! # fn main() -> Result<(), soma_som_ring::RingEngineError> {
102//! let mut engine = RingEngine::new(TimingConfig::default());
103//! engine.set_all_processors(|| Box::new(Echo));
104//! engine.genesis(b"seed")?;
105//!
106//! let cycle = engine.cycle()?;
107//! assert_eq!(cycle.cycle_index, 1);
108//! assert_eq!(cycle.crossing_records.len(), 12);
109//!
110//! // Run 3 more cycles in a batch.
111//! let reports = engine.run_cycles(3);
112//! assert_eq!(reports.len(), 3);
113//! assert!(reports.iter().all(|r| r.is_ok()));
114//! assert_eq!(engine.cycle_index(), 4);
115//! # Ok(())
116//! # }
117//! ```
118
119pub mod boundary;
120pub mod command;
121pub mod dispatch;
122pub mod engine;
123pub mod error;
124pub mod federation;
125pub mod passthrough;
126pub mod processor;
127
128// ── Primary re-exports ──────────────────────────────────────────────────
129
130pub use engine::{
131    CommandRegistry, CommandRegistryEntry, CycleObserver, CycleReport, GenesisReport, NoOpObserver,
132    RingEngine,
133};
134// Schema types are declared on `soma_som_core::Extension`. Consumers wanting to
135// build a `CommandSchema` import it directly from `soma-som-core`:
136//
137//     use soma_som_core::{CommandSchema, SchemaField, SchemaFieldType};
138//
139// `soma-som-ring` deliberately does NOT re-export these to keep the
140// dependency direction honest (the trait surface owns the type) and to avoid
141// a hidden semver coupling across the two crate boundaries.
142pub use error::{RingEngineError, RingEngineResult};
143pub use federation::{FederationBridge, LocalDescriptor};
144pub use command::{
145    COMMAND_PREFIX, CommandResult, CommandStatus, RESULT_PREFIX, RingCommand, VIEW_PREFIX,
146    ViewIntent, inject_command, inject_view_intent, validate_payload,
147};
148pub use dispatch::{CommandDispatchError, CommandDispatcher, PolicyProvider};
149pub use passthrough::PassthroughProcessor;
150pub use processor::RingProcessor;