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;