Skip to main content

pipecircuit_visualization/program/
mod.rs

1//! Inspection of compiled declarations; no second list of roles or bindings.
2use crate::DiagramTarget;
3use core::{any::type_name, marker::PhantomData};
4use pipecircuit::{
5    BindingDescription, DescribeBindings, DescribeRoster, PipeDescription, PipeRoster, Switchboard,
6};
7mod html;
8mod render;
9
10/// Experimental development scaffolding; not ready for application consumption.
11///
12/// A compiled program boundary view. Routing remains the board's runtime decision.
13/// Rendered type labels abbreviate paths; accessors retain compiler-qualified names.
14///
15/// Equal-looking roles do not make two rosters interchangeable:
16/// ```compile_fail,E0271
17/// use pipecircuit::{Pipe, Switchboard};
18/// use pipecircuit_visualization::ProgramDiagram;
19/// struct Protocol;
20/// impl Pipe for Protocol { type In<'a> = (); type Out<'a> = (); }
21/// pipecircuit::pipe_roster! { mod first { client: super::Protocol } }
22/// pipecircuit::pipe_roster! { mod second { client: super::Protocol } }
23/// struct Board;
24/// impl Switchboard for Board {
25///     type From<'a> = first::From<'a>;
26///     type To<'a> = first::To<'a>;
27///     fn route<'a>(&mut self, from: Self::From<'a>) -> Self::To<'a> {
28///         match from { first::From::Client(()) => first::To::Client(()) }
29///     }
30/// }
31/// let mismatched = ProgramDiagram::<second::Schema>::for_board::<Board>();
32/// ```
33pub struct ProgramDiagram<Roster: DescribeRoster> {
34    board: &'static str,
35    pipes: Vec<PipeDescription>,
36    roster: PhantomData<fn() -> Roster>,
37}
38impl<Roster: DescribeRoster> ProgramDiagram<Roster> {
39    pub fn for_board<Board: Switchboard>() -> Self
40    where
41        Roster: for<'a> PipeRoster<From<'a> = Board::From<'a>, To<'a> = Board::To<'a>>,
42    {
43        Self {
44            board: type_name::<Board>(),
45            pipes: Roster::describe_pipes().collect(),
46            roster: PhantomData,
47        }
48    }
49    pub fn board(&self) -> &'static str {
50        self.board
51    }
52    pub fn pipes(&self) -> &[PipeDescription] {
53        &self.pipes
54    }
55    /// A standalone browser view with expandable declarations and an optional
56    /// physical layer. No scripts, network resources or live runtime polling.
57    pub fn render_html(&self) -> String {
58        html::render(self.board, &self.pipes, None)
59    }
60    pub fn render(&self, target: DiagramTarget) -> String {
61        render::render(self.board, &self.pipes, None, target)
62    }
63    /// The binding inventory must name this exact roster; unrelated installations
64    /// cannot be combined merely because their fields have equal spellings.
65    pub fn installed<Bindings: DescribeBindings<Roster = Roster>>(
66        &self,
67        bindings: &Bindings,
68    ) -> InstallationDiagram {
69        InstallationDiagram {
70            board: self.board,
71            pipes: self.pipes.clone(),
72            bindings: bindings.describe_bindings().collect(),
73        }
74    }
75}
76
77/// Experimental development scaffolding; not ready for application consumption.
78///
79/// Concrete compiled carrier types, not guessed placement, occupancy or health.
80/// Source links identify declarations; type-name labels are not stable identifiers.
81pub struct InstallationDiagram {
82    board: &'static str,
83    pipes: Vec<PipeDescription>,
84    bindings: Vec<BindingDescription>,
85}
86impl InstallationDiagram {
87    pub fn bindings(&self) -> &[BindingDescription] {
88        &self.bindings
89    }
90    /// A standalone browser view with expandable declarations and an optional
91    /// physical layer. No scripts, network resources or live runtime polling.
92    pub fn render_html(&self) -> String {
93        html::render(self.board, &self.pipes, Some(&self.bindings))
94    }
95    pub fn render(&self, target: DiagramTarget) -> String {
96        render::render(self.board, &self.pipes, Some(&self.bindings), target)
97    }
98}
99#[cfg(test)]
100mod tests;