pipecircuit-visualization 0.0.4

Code-derived architectural visualizations for Pipecircuit programs
Documentation
//! Inspection of compiled declarations; no second list of roles or bindings.
use crate::DiagramTarget;
use core::{any::type_name, marker::PhantomData};
use pipecircuit::{
    BindingDescription, DescribeBindings, DescribeRoster, PipeDescription, PipeRoster, Switchboard,
};
mod html;
mod render;

/// Experimental development scaffolding; not ready for application consumption.
///
/// A compiled program boundary view. Routing remains the board's runtime decision.
/// Rendered type labels abbreviate paths; accessors retain compiler-qualified names.
///
/// Equal-looking roles do not make two rosters interchangeable:
/// ```compile_fail,E0271
/// use pipecircuit::{Pipe, Switchboard};
/// use pipecircuit_visualization::ProgramDiagram;
/// struct Protocol;
/// impl Pipe for Protocol { type In<'a> = (); type Out<'a> = (); }
/// pipecircuit::pipe_roster! { mod first { client: super::Protocol } }
/// pipecircuit::pipe_roster! { mod second { client: super::Protocol } }
/// struct Board;
/// impl Switchboard for Board {
///     type From<'a> = first::From<'a>;
///     type To<'a> = first::To<'a>;
///     fn route<'a>(&mut self, from: Self::From<'a>) -> Self::To<'a> {
///         match from { first::From::Client(()) => first::To::Client(()) }
///     }
/// }
/// let mismatched = ProgramDiagram::<second::Schema>::for_board::<Board>();
/// ```
pub struct ProgramDiagram<Roster: DescribeRoster> {
    board: &'static str,
    pipes: Vec<PipeDescription>,
    roster: PhantomData<fn() -> Roster>,
}
impl<Roster: DescribeRoster> ProgramDiagram<Roster> {
    pub fn for_board<Board: Switchboard>() -> Self
    where
        Roster: for<'a> PipeRoster<From<'a> = Board::From<'a>, To<'a> = Board::To<'a>>,
    {
        Self {
            board: type_name::<Board>(),
            pipes: Roster::describe_pipes().collect(),
            roster: PhantomData,
        }
    }
    pub fn board(&self) -> &'static str {
        self.board
    }
    pub fn pipes(&self) -> &[PipeDescription] {
        &self.pipes
    }
    /// A standalone browser view with expandable declarations and an optional
    /// physical layer. No scripts, network resources or live runtime polling.
    pub fn render_html(&self) -> String {
        html::render(self.board, &self.pipes, None)
    }
    pub fn render(&self, target: DiagramTarget) -> String {
        render::render(self.board, &self.pipes, None, target)
    }
    /// The binding inventory must name this exact roster; unrelated installations
    /// cannot be combined merely because their fields have equal spellings.
    pub fn installed<Bindings: DescribeBindings<Roster = Roster>>(
        &self,
        bindings: &Bindings,
    ) -> InstallationDiagram {
        InstallationDiagram {
            board: self.board,
            pipes: self.pipes.clone(),
            bindings: bindings.describe_bindings().collect(),
        }
    }
}

/// Experimental development scaffolding; not ready for application consumption.
///
/// Concrete compiled carrier types, not guessed placement, occupancy or health.
/// Source links identify declarations; type-name labels are not stable identifiers.
pub struct InstallationDiagram {
    board: &'static str,
    pipes: Vec<PipeDescription>,
    bindings: Vec<BindingDescription>,
}
impl InstallationDiagram {
    pub fn bindings(&self) -> &[BindingDescription] {
        &self.bindings
    }
    /// A standalone browser view with expandable declarations and an optional
    /// physical layer. No scripts, network resources or live runtime polling.
    pub fn render_html(&self) -> String {
        html::render(self.board, &self.pipes, Some(&self.bindings))
    }
    pub fn render(&self, target: DiagramTarget) -> String {
        render::render(self.board, &self.pipes, Some(&self.bindings), target)
    }
}
#[cfg(test)]
mod tests;