rigless 0.0.2

Virtual hardware-in-the-loop runtime: run unmodified firmware on an emulated MCU with every peripheral, chip model, and unit of virtual time in Rust; needs the rigless-sidecar package from rigless-labs/qemu
Documentation
// Copyright (c) 2026 Rigless Labs.
// SPDX-License-Identifier: Apache-2.0
//! What one sidecar process is started as, in the vocabulary of the engine and not of any SoC.
//!
//! A machine module builds a [`SidecarCommand`] for each of its targets, naming the engine's machine, its
//! core, and the properties that machine declares. The core adds what the protocol needs and nothing
//! else: the control block, the firmware, the instruction rate, and the debugger.

use std::fmt;

/// The machine property the core sets itself, naming the object that holds the control block.
pub const CONTROL_BLOCK_PROPERTY: &str = "control-block";

/// What one target's engine is started as, as its machine module renders it.
///
/// The properties are the machine's own, in the order the machine module lists them; the runtime appends
/// [`CONTROL_BLOCK_PROPERTY`] and never reads or writes another.
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct SidecarCommand {
    /// The name of the binary on `PATH` when nothing else names one.
    pub binary: &'static str,
    /// The engine's machine, which the machine module mirrors field for field.
    pub machine: &'static str,
    /// The core the machine runs.
    pub cpu: &'static str,
    /// The machine's properties, as it spells them.
    pub properties: Vec<Property>,
    /// Every instruction takes `2 ^ icount_shift` nanoseconds; the machine module owns the rate.
    pub icount_shift: u32,
    /// The regions a second address also reaches, which is the board's wiring and not the core's business.
    pub aliases: Vec<Alias>,
}

/// One property of the engine's machine, with the value the machine module gives it.
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Property {
    /// The name the machine declares.
    pub name: &'static str,
    /// The value, rendered as the machine's own parser takes it.
    pub value: String,
}

impl Property {
    /// The property `name` set to `value`.
    pub fn new(name: &'static str, value: impl fmt::Display) -> Self {
        Self {
            name,
            value: value.to_string(),
        }
    }
}

impl fmt::Display for Property {
    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(formatter, "{}={}", self.name, self.value)
    }
}

/// A second address the whole of a memory region also answers at.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct Alias {
    /// The region's name in the target's memory map.
    pub region: &'static str,
    /// Where the same bytes appear again.
    pub at: u64,
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn a_property_renders_as_the_machine_spells_it() {
        assert_eq!(Property::new("num-irq", 91u16).to_string(), "num-irq=91");
        assert_eq!(
            Property::new("control-block", "cb").to_string(),
            "control-block=cb"
        );
    }
}