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