graphs_tui/lib.rs
1#![allow(clippy::too_many_arguments)]
2#![allow(clippy::collapsible_else_if)]
3
4//! graphs-tui - Terminal renderer for Mermaid and D2 diagrams
5//!
6//! # Mermaid Flowchart Example
7//! ```
8//! use graphs_tui::{render_mermaid_to_tui, RenderOptions};
9//!
10//! let input = "flowchart LR\nA[Start] --> B[End]";
11//! let result = render_mermaid_to_tui(input, RenderOptions::default()).unwrap();
12//! println!("{}", result.output);
13//! for warning in &result.warnings {
14//! eprintln!("Warning: {warning}");
15//! }
16//! ```
17//!
18//! # State Diagram Example
19//! ```
20//! use graphs_tui::{render_state_diagram, RenderOptions};
21//!
22//! let input = "stateDiagram-v2\n [*] --> Idle\n Idle --> Running";
23//! let result = render_state_diagram(input, RenderOptions::default()).unwrap();
24//! println!("{}", result.output);
25//! ```
26//!
27//! # Pie Chart Example
28//! ```
29//! use graphs_tui::{render_pie_chart, RenderOptions};
30//!
31//! let input = "pie\n \"Chrome\" : 65\n \"Firefox\" : 35";
32//! let result = render_pie_chart(input, RenderOptions::default()).unwrap();
33//! println!("{}", result.output);
34//! ```
35//!
36//! # D2 Example
37//! ```
38//! use graphs_tui::{render_d2_to_tui, RenderOptions};
39//!
40//! let input = "A -> B: connection";
41//! let result = render_d2_to_tui(input, RenderOptions::default()).unwrap();
42//! println!("{}", result.output);
43//! ```
44//!
45//! # Sequence Diagram Example
46//! ```
47//! use graphs_tui::{render_sequence_diagram, RenderOptions};
48//!
49//! let input = "sequenceDiagram\n Alice->>Bob: Hello\n Bob-->>Alice: Hi!";
50//! let result = render_sequence_diagram(input, RenderOptions::default()).unwrap();
51//! println!("{}", result.output);
52//! ```
53//!
54//! # Auto-detect Format
55//! ```
56//! use graphs_tui::{render_diagram, RenderOptions};
57//!
58//! let mermaid_input = "flowchart LR\nA --> B";
59//! let d2_input = "A -> B";
60//!
61//! // Automatically detects format
62//! let _ = render_diagram(mermaid_input, RenderOptions::default());
63//! let _ = render_diagram(d2_input, RenderOptions::default());
64//! ```
65
66mod d2_parser;
67mod error;
68mod grid;
69mod layout;
70mod parser;
71mod pie_parser;
72mod renderer;
73mod seq_parser;
74mod state_parser;
75mod types;
76
77pub use error::MermaidError;
78pub use layout::{compute_layout, compute_layout_with_options};
79pub use types::{
80 DiagramWarning, Direction, Edge, EdgeStyle, Graph, Node, NodeId, NodeShape, RenderOptions,
81 RenderResult, Subgraph,
82};
83
84use d2_parser::parse_d2;
85use parser::parse_mermaid;
86use pie_parser::{parse_pie_chart as parse_pie, render_pie_chart as render_pie};
87use renderer::render_graph;
88use seq_parser::{parse_sequence_diagram as parse_seq, render_sequence_diagram as render_seq};
89use state_parser::parse_state_diagram;
90
91/// Diagram format
92#[derive(Debug, Clone, Copy, PartialEq, Eq)]
93pub enum DiagramFormat {
94 /// Mermaid flowchart syntax
95 Mermaid,
96 /// Mermaid state diagram
97 StateDiagram,
98 /// Mermaid sequence diagram
99 SequenceDiagram,
100 /// Mermaid pie chart
101 PieChart,
102 /// D2 diagram language
103 D2,
104}
105
106/// Detect the diagram format from input
107pub fn detect_format(input: &str) -> DiagramFormat {
108 let trimmed = input.trim();
109 let lower = trimmed.to_lowercase();
110
111 // Check for specific diagram types first
112 if lower.starts_with("sequencediagram") {
113 return DiagramFormat::SequenceDiagram;
114 }
115 if lower.starts_with("statediagram") {
116 return DiagramFormat::StateDiagram;
117 }
118 if lower.starts_with("pie") {
119 return DiagramFormat::PieChart;
120 }
121
122 // Mermaid flowchart indicators
123 if trimmed.starts_with("flowchart")
124 || trimmed.starts_with("graph ")
125 || trimmed.contains("-->")
126 || trimmed.contains("-.-")
127 || trimmed.contains("==>")
128 {
129 return DiagramFormat::Mermaid;
130 }
131
132 // D2 uses different arrow syntax
133 // D2: ->, <-, <->, --
134 // Mermaid: -->, <--, <-->, ---
135
136 DiagramFormat::D2
137}
138
139/// Render diagram with auto-detection of format
140///
141/// # Arguments
142/// * `input` - Diagram syntax string (Mermaid, State, Pie, or D2)
143/// * `options` - Rendering options
144///
145/// # Returns
146/// * `Ok(RenderResult)` - Rendered diagram with any warnings
147/// * `Err(MermaidError)` - Parse or layout error
148pub fn render_diagram(input: &str, options: RenderOptions) -> Result<RenderResult, MermaidError> {
149 match detect_format(input) {
150 DiagramFormat::Mermaid => render_mermaid_to_tui(input, options),
151 DiagramFormat::StateDiagram => render_state_diagram(input, options),
152 DiagramFormat::SequenceDiagram => render_sequence_diagram(input, options),
153 DiagramFormat::PieChart => render_pie_chart(input, options),
154 DiagramFormat::D2 => render_d2_to_tui(input, options),
155 }
156}
157
158/// Render mermaid flowchart syntax to terminal-displayable text
159///
160/// # Arguments
161/// * `input` - Mermaid flowchart syntax string
162/// * `options` - Rendering options (ASCII mode, max width)
163///
164/// # Returns
165/// * `Ok(RenderResult)` - Rendered diagram with any warnings
166/// * `Err(MermaidError)` - Parse or layout error
167pub fn render_mermaid_to_tui(
168 input: &str,
169 options: RenderOptions,
170) -> Result<RenderResult, MermaidError> {
171 let mut graph = parse_mermaid(input)?;
172 let mut warnings = compute_layout_with_options(&mut graph, &options);
173 Ok(RenderResult {
174 output: render_graph(&graph, &options, &mut warnings),
175 warnings,
176 })
177}
178
179/// Render mermaid state diagram to terminal-displayable text
180///
181/// # Arguments
182/// * `input` - Mermaid state diagram syntax string
183/// * `options` - Rendering options (ASCII mode, max width)
184///
185/// # Returns
186/// * `Ok(RenderResult)` - Rendered diagram with any warnings
187/// * `Err(MermaidError)` - Parse or layout error
188pub fn render_state_diagram(
189 input: &str,
190 options: RenderOptions,
191) -> Result<RenderResult, MermaidError> {
192 let mut graph = parse_state_diagram(input)?;
193 let mut warnings = compute_layout_with_options(&mut graph, &options);
194 Ok(RenderResult {
195 output: render_graph(&graph, &options, &mut warnings),
196 warnings,
197 })
198}
199
200/// Render mermaid pie chart to terminal-displayable text
201///
202/// Pie charts are rendered as horizontal bar charts in terminal.
203///
204/// # Arguments
205/// * `input` - Mermaid pie chart syntax string
206/// * `options` - Rendering options
207///
208/// # Returns
209/// * `Ok(RenderResult)` - Rendered chart with any warnings
210/// * `Err(MermaidError)` - Parse error
211pub fn render_pie_chart(input: &str, options: RenderOptions) -> Result<RenderResult, MermaidError> {
212 let chart = parse_pie(input)?;
213 Ok(RenderResult {
214 output: render_pie(&chart, &options),
215 warnings: Vec::new(),
216 })
217}
218
219/// Render D2 diagram syntax to terminal-displayable text
220///
221/// # Arguments
222/// * `input` - D2 diagram syntax string
223/// * `options` - Rendering options (ASCII mode, max width)
224///
225/// # Returns
226/// * `Ok(RenderResult)` - Rendered diagram with any warnings
227/// * `Err(MermaidError)` - Parse or layout error
228pub fn render_d2_to_tui(input: &str, options: RenderOptions) -> Result<RenderResult, MermaidError> {
229 let mut graph = parse_d2(input)?;
230 let mut warnings = compute_layout_with_options(&mut graph, &options);
231 Ok(RenderResult {
232 output: render_graph(&graph, &options, &mut warnings),
233 warnings,
234 })
235}
236
237/// Render mermaid sequence diagram to terminal-displayable text
238///
239/// # Arguments
240/// * `input` - Mermaid sequence diagram syntax string
241/// * `options` - Rendering options (ASCII mode, max width)
242///
243/// # Returns
244/// * `Ok(RenderResult)` - Rendered diagram with any warnings
245/// * `Err(MermaidError)` - Parse error
246pub fn render_sequence_diagram(
247 input: &str,
248 options: RenderOptions,
249) -> Result<RenderResult, MermaidError> {
250 let diagram = parse_seq(input)?;
251 Ok(RenderResult {
252 output: render_seq(&diagram, &options),
253 warnings: Vec::new(),
254 })
255}