mmdflux/lib.rs
1//! mmdflux — Mermaid diagrams to text, SVG, and MMDS
2//!
3//! mmdflux parses [Mermaid](https://mermaid.js.org/) diagram syntax and
4//! renders it as Unicode/ASCII text, SVG, or structured JSON ([MMDS](https://mmds.dev/)).
5//! Supported diagram types: **flowchart**, **class**, and **sequence**.
6//!
7//! # High-Level API
8//!
9//! Most consumers only need three functions and two config types:
10//!
11//! - [`render_diagram`] — detect, parse, and render in one call
12//! - [`detect_diagram`] — detect the diagram type without rendering
13//! - [`validate_diagram`] — parse and return structured JSON diagnostics
14//! - [`OutputFormat`] — `Text`, `Ascii`, `Svg`, or `Mmds`
15//! - [`RenderConfig`] — layout engine, routing, padding, color, and more
16//!
17//! ```
18//! use mmdflux::{OutputFormat, RenderConfig, render_diagram};
19//!
20//! let input = "graph TD\n A[Collect] --> B[Render]";
21//!
22//! // Render as Unicode text
23//! let text = render_diagram(input, OutputFormat::Text, &RenderConfig::default()).unwrap();
24//! println!("{text}");
25//!
26//! // Render as SVG
27//! let svg = render_diagram(input, OutputFormat::Svg, &RenderConfig::default()).unwrap();
28//! assert!(svg.contains("<svg"));
29//!
30//! // Render as MMDS JSON (structured interchange format)
31//! let json = render_diagram(input, OutputFormat::Mmds, &RenderConfig::default()).unwrap();
32//! assert!(json.contains("\"diagram_type\""));
33//! ```
34//!
35//! ## Customizing output
36//!
37//! Use [`RenderConfig`] to control layout direction, engine selection, edge
38//! routing, padding, color mode, and more:
39//!
40//! ```
41//! use mmdflux::{OutputFormat, RenderConfig, render_diagram};
42//! use mmdflux::LayoutConfig;
43//! use mmdflux::format::RoutingStyle;
44//!
45//! let config = RenderConfig {
46//! routing_style: Some(RoutingStyle::Direct),
47//! padding: Some(2),
48//! layout: LayoutConfig {
49//! rank_sep: 30.0,
50//! ..LayoutConfig::default()
51//! },
52//! ..RenderConfig::default()
53//! };
54//!
55//! let output = render_diagram("graph LR\n A-->B-->C", OutputFormat::Text, &config).unwrap();
56//! println!("{output}");
57//! ```
58//!
59//! ## Validation
60//!
61//! [`validate_diagram`] returns structured JSON diagnostics suitable for
62//! editor integrations and CI pipelines:
63//!
64//! ```
65//! use mmdflux::validate_diagram;
66//!
67//! let result = validate_diagram("graph TD\n A-->B");
68//! let json: serde_json::Value = serde_json::from_str(&result).unwrap();
69//! assert_eq!(json["valid"], true);
70//! ```
71//!
72//! # Low-Level API
73//!
74//! For adapters, tooling, or workflows that need explicit control over the
75//! detect → parse → payload → render pipeline, the low-level API provides:
76//!
77//! - [`builtins::default_registry`] — the built-in diagram registry
78//! - [`registry`] — [`DiagramRegistry`](registry::DiagramRegistry),
79//! [`DiagramInstance`](registry::DiagramInstance), and
80//! [`ParsedDiagram`](registry::ParsedDiagram) traits
81//! - [`payload`] — the [`payload::Diagram`] enum returned by
82//! [`ParsedDiagram::into_payload`](registry::ParsedDiagram::into_payload)
83//! - [`graph`] — graph-family IR types ([`Graph`](graph::Graph),
84//! [`Node`](graph::Node), [`Edge`](graph::Edge),
85//! [`Shape`](graph::Shape), [`Direction`](graph::Direction))
86//! - [`timeline`] — timeline-family types
87//! ([`Sequence`](timeline::Sequence))
88//! - [`mmds`] — MMDS parsing, hydration to [`graph::Graph`],
89//! profile negotiation, and Mermaid regeneration
90//!
91//! ```no_run
92//! use mmdflux::builtins::default_registry;
93//! use mmdflux::payload::Diagram;
94//!
95//! let input = "graph TD\n A[Draft] --> B[Published]";
96//! let registry = default_registry();
97//!
98//! // Detect diagram type
99//! let resolved = registry.resolve(input).expect("should detect diagram type");
100//! println!("detected: {} ({:?})", resolved.diagram_id(), resolved.family());
101//!
102//! // Parse and build payload
103//! let instance = registry.create(resolved.diagram_id()).unwrap();
104//! let payload = instance
105//! .parse(input).unwrap()
106//! .into_payload().unwrap();
107//!
108//! // Inspect the payload
109//! match payload {
110//! Diagram::Flowchart(graph) => {
111//! println!("flowchart with {} nodes", graph.nodes.len());
112//! }
113//! Diagram::Class(graph) => {
114//! println!("class diagram with {} nodes", graph.nodes.len());
115//! }
116//! Diagram::Sequence(seq) => {
117//! println!("sequence with {} participants", seq.participants.len());
118//! }
119//! }
120//! ```
121//!
122//! ## MMDS interchange
123//!
124//! [MMDS](https://mmds.dev/) is a structured JSON format for diagram geometry.
125//! Use the [`mmds`] module to parse MMDS input, hydrate it to a
126//! [`graph::Graph`], or regenerate Mermaid source. To render MMDS input to
127//! text/SVG, pass it to [`render_diagram`] which auto-detects MMDS:
128//!
129//! ```
130//! use mmdflux::mmds::{from_str, generate_mermaid_from_str};
131//!
132//! let mmds_json = r#"{
133//! "version": 1,
134//! "profiles": ["mmds-core-v1"],
135//! "defaults": {
136//! "node": { "shape": "rectangle" },
137//! "edge": { "stroke": "solid", "arrow_start": "none", "arrow_end": "normal", "minlen": 1 }
138//! },
139//! "geometry_level": "layout",
140//! "metadata": {
141//! "diagram_type": "flowchart",
142//! "direction": "TD",
143//! "bounds": { "width": 100.0, "height": 80.0 }
144//! },
145//! "nodes": [
146//! { "id": "A", "label": "Start", "position": { "x": 50.0, "y": 20.0 },
147//! "size": { "width": 50.0, "height": 20.0 } },
148//! { "id": "B", "label": "End", "position": { "x": 50.0, "y": 60.0 },
149//! "size": { "width": 50.0, "height": 20.0 } }
150//! ],
151//! "edges": [{ "id": "e0", "source": "A", "target": "B" }]
152//! }"#;
153//!
154//! // Hydrate to graph IR
155//! let graph = from_str(mmds_json).unwrap();
156//! assert_eq!(graph.nodes.len(), 2);
157//!
158//! // Regenerate Mermaid source
159//! let mermaid = generate_mermaid_from_str(mmds_json).unwrap();
160//! assert!(mermaid.contains("flowchart TD"));
161//! ```
162
163pub mod builtins;
164mod diagrams;
165mod engines;
166pub mod errors;
167pub mod format;
168mod frontends;
169pub mod graph;
170mod mermaid;
171pub mod mmds;
172pub mod payload;
173pub mod registry;
174mod render;
175// Facade functions and config_input are re-exported below as public API.
176pub(crate) mod runtime;
177pub mod simplification;
178pub mod timeline;
179
180// Public re-exports from public modules (convenience aliases).
181// Re-exports from public modules for convenience at crate root.
182/// Algorithm identifier (e.g., `Layered`, `Mrtree`) used in engine selection.
183pub use engines::graph::AlgorithmId;
184/// Combined engine + algorithm identifier for explicit layout engine selection.
185pub use engines::graph::EngineAlgorithmId;
186/// Engine identifier (e.g., `Flux`, `Mermaid`, `Elk`).
187pub use engines::graph::EngineId;
188pub use errors::RenderError;
189/// Policy for resolving `--color auto` in CLI/WASM adapters.
190pub use format::ColorWhen;
191pub use format::OutputFormat;
192/// Text output color mode (plain, styled, or ANSI).
193pub use format::TextColorMode;
194pub use runtime::config::RenderConfig;
195/// Layout configuration for the Sugiyama hierarchical engine.
196pub use runtime::config::{LabelDummyStrategy, LayoutConfig, LayoutDirection, Ranker};
197/// Serde-friendly config input for JSON consumers (WASM, API).
198pub use runtime::config_input::RuntimeConfigInput;
199/// Apply default SVG surface settings (curve, engine) when format is SVG.
200pub use runtime::config_input::apply_svg_surface_defaults;
201/// Detect the diagram type from input text.
202pub use runtime::detect_diagram;
203/// Detect, parse, and render a diagram in one call.
204pub use runtime::render_diagram;
205/// Validate input and return structured JSON diagnostics.
206pub use runtime::validate_diagram;
207
208// Residual crate-local tests stay narrowly scoped to cross-pipeline coverage.
209#[cfg(test)]
210mod internal_tests;