soma-som-ring 0.1.0

Standalone ring execution engine for soma(som): cycle lifecycle, extension registration, boundary mediation
Documentation
// SPDX-License-Identifier: LGPL-3.0-only
//! `custom_processor` — implement `RingProcessor` for a domain-specific case.
//!
//! Demonstrates how to plug a stateful, domain-aware processor into the ring.
//! `CounterProcessor` keeps a per-instance cycle count and emits two markers
//! into the output Tree (`counter.cycle`, `counter.total`) — the kind of
//! pattern you would use for a metrics / instrumentation processor.
//!
//! Run with:
//!
//!     cargo run --example custom_processor -p soma-som-ring

use soma_som_core::timing::TimingConfig;
use soma_som_core::quad::{Quad, Tree};
use soma_som_core::types::UnitId;
use soma_som_ring::{RingEngine, RingEngineError, RingProcessor};

/// A stateful processor that counts how many cycles it has handled, and
/// emits the count + the cycle index into its output Tree.
struct CounterProcessor {
    name: &'static str,
    count: u64,
}

impl CounterProcessor {
    fn new(name: &'static str) -> Self {
        Self { name, count: 0 }
    }
}

impl RingProcessor for CounterProcessor {
    fn process(
        &mut self,
        unit: UnitId,
        cycle: u64,
        input: &Quad,
        _data: &Quad,
    ) -> Result<Quad, RingEngineError> {
        self.count += 1;

        let mut tree: Tree = input.tree.clone();
        tree.insert("counter.unit".into(), format!("{unit:?}").into_bytes());
        tree.insert("counter.cycle".into(), cycle.to_le_bytes().to_vec());
        tree.insert("counter.total".into(), self.count.to_le_bytes().to_vec());
        tree.insert("counter.name".into(), self.name.as_bytes().to_vec());

        Ok(Quad::new(input.root, input.pointer, tree))
    }
}

fn main() -> Result<(), RingEngineError> {
    let mut engine = RingEngine::new(TimingConfig::default());

    // One CounterProcessor per unit — each carries its own state.
    // We use the unit's debug name as the processor's logical name.
    engine.set_processor(UnitId::FU, Box::new(CounterProcessor::new("FU-counter")));
    engine.set_processor(UnitId::MU, Box::new(CounterProcessor::new("MU-counter")));
    engine.set_processor(UnitId::CU, Box::new(CounterProcessor::new("CU-counter")));
    engine.set_processor(UnitId::OU, Box::new(CounterProcessor::new("OU-counter")));
    engine.set_processor(UnitId::SU, Box::new(CounterProcessor::new("SU-counter")));
    engine.set_processor(UnitId::HU, Box::new(CounterProcessor::new("HU-counter")));

    engine.genesis(b"counter-example-seed")?;

    // Run 5 cycles and report on every other one.
    for _ in 0..5 {
        let report = engine.cycle()?;
        println!(
            "cycle {} — crossings={} elapsed_ms={}",
            report.cycle_index,
            report.crossing_records.len(),
            report.elapsed_ms,
        );
    }

    println!("total cycles executed: {}", engine.cycle_index());
    println!(
        "active units after run: {} (each ran its own CounterProcessor)",
        engine.active_units().len(),
    );

    Ok(())
}