Skip to main content

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}