use ratatui::style::Style;
mod canvas;
mod drawing;
mod flow;
mod ordering;
mod painter;
mod placement;
pub(in crate::tui) use canvas::{Canvas, Cls as CellClass, D, L, R, U};
pub(in crate::tui) use drawing::{draw_box, draw_seq_text, fit_label, wrap_label};
pub(in crate::tui) use flow::{
art_from_layout, flow_labels_fit, flow_wrap_widths, layout_canvas, layout_flow, NodeExtra,
Placed,
};
pub(in crate::tui) use painter::{
GraphArt, GraphStyles, Oversize, MAX_CANVAS_CELLS, MAX_LABEL, MAX_LINES, PAD, WRAP_WIDTH,
};
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub(in crate::tui) struct NodeStyle {
pub(in crate::tui) border: Style,
pub(in crate::tui) text: Style,
}
impl NodeStyle {
pub(in crate::tui) const fn new(border: Style, text: Style) -> Self {
Self { border, text }
}
pub(in crate::tui) const fn uniform(style: Style) -> Self {
Self {
border: style,
text: style,
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub(in crate::tui) enum NodeShape {
Rect,
Round,
Diamond,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub(in crate::tui) enum Direction {
TopDown,
BottomUp,
LeftRight,
RightLeft,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub(in crate::tui) enum RankOrdering {
PreserveInput,
MinimizeCrossings,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub(in crate::tui) enum EdgeHead {
None,
Arrow,
Circle,
Cross,
Triangle,
DiamondFill,
DiamondOpen,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub(in crate::tui) enum EdgeLine {
Solid,
Dotted,
Thick,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub(in crate::tui) enum TextAlignment {
Left,
Center,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub(in crate::tui) struct Compartment {
pub(in crate::tui) lines: Vec<String>,
pub(in crate::tui) alignment: TextAlignment,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub(in crate::tui) struct Node {
pub(in crate::tui) label: String,
pub(in crate::tui) shape: NodeShape,
pub(in crate::tui) style: NodeStyle,
}
impl Node {
pub(in crate::tui) fn rectangular(label: impl Into<String>, style: NodeStyle) -> Self {
Self {
label: label.into(),
shape: NodeShape::Rect,
style,
}
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub(in crate::tui) struct Edge {
pub(in crate::tui) from: usize,
pub(in crate::tui) to: usize,
pub(in crate::tui) label: Option<String>,
pub(in crate::tui) head_to: EdgeHead,
pub(in crate::tui) head_from: EdgeHead,
pub(in crate::tui) line: EdgeLine,
}
impl Edge {
pub(in crate::tui) fn directed(from: usize, to: usize) -> Self {
Self {
from,
to,
label: None,
head_to: EdgeHead::Arrow,
head_from: EdgeHead::None,
line: EdgeLine::Solid,
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub(in crate::tui) enum GraphError {
InvalidEdgeEndpoint,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub(in crate::tui) struct Graph {
pub(in crate::tui) nodes: Vec<Node>,
pub(in crate::tui) edges: Vec<Edge>,
pub(in crate::tui) direction: Direction,
pub(in crate::tui) rank_ordering: RankOrdering,
}
impl Graph {
pub(in crate::tui) fn top_down(
nodes: Vec<Node>,
edges: Vec<Edge>,
rank_ordering: RankOrdering,
) -> Result<Self, GraphError> {
Self::from_parts(nodes, edges, Direction::TopDown, rank_ordering)
}
pub(in crate::tui) fn from_parts(
nodes: Vec<Node>,
edges: Vec<Edge>,
direction: Direction,
rank_ordering: RankOrdering,
) -> Result<Self, GraphError> {
if edges
.iter()
.any(|edge| edge.from >= nodes.len() || edge.to >= nodes.len())
{
return Err(GraphError::InvalidEdgeEndpoint);
}
Ok(Self {
nodes,
edges,
direction,
rank_ordering,
})
}
pub(in crate::tui) fn render(&self, edge_style: Style) -> Result<GraphArt, Oversize> {
let styles = GraphStyles::for_nodes(&self.nodes, edge_style);
layout_flow(self, &styles, None)
}
pub(in crate::tui) fn ranks(&self) -> Vec<usize> {
drawing::compute_ranks(self)
}
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub(in crate::tui) struct NodeRect {
pub(in crate::tui) x: usize,
pub(in crate::tui) y: usize,
pub(in crate::tui) width: usize,
pub(in crate::tui) height: usize,
}
#[cfg(test)]
#[path = "terminal_graph_tests.rs"]
mod tests;