1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
//! Render Mermaid diagrams in rustdoc as inline SVG.
//!
//! `merman-rustdoc` is a proc-macro integration for crates that want Mermaid diagrams in API docs
//! without loading Mermaid JavaScript in the browser. The [`macro@merman`] attribute reads Mermaid
//! code fences and `include_mmd!` lines from item documentation, renders them with Merman during
//! `cargo doc`, and writes the resulting SVG back into the generated rustdoc page.
//!
//! # Install
//!
//! Use a normal dependency for the simplest setup:
//!
//! ```toml
//! [dependencies]
//! merman-rustdoc = "0.7"
//! ```
//!
//! This works for local `cargo doc` and for docs.rs because the examples below use
//! `cfg_attr(doc, ...)`. The macro only expands during rustdoc builds, but Cargo will still compile
//! the dependency during ordinary builds.
//!
//! If you want ordinary builds to avoid compiling `merman-rustdoc`, make it optional behind a
//! documentation feature:
//!
//! ```toml
//! [dependencies]
//! merman-rustdoc = { version = "0.7", optional = true }
//!
//! [features]
//! doc-diagrams = ["dep:merman-rustdoc"]
//!
//! [package.metadata.docs.rs]
//! features = ["doc-diagrams"]
//! ```
//!
//! With this optional setup, build docs locally with:
//!
//! ```sh
//! cargo doc --features doc-diagrams
//! ```
//!
//! # Quickstart
//!
//! Put the attribute on any item whose docs contain a Mermaid fence:
//!
//! ````rust
//! #[cfg_attr(doc, merman_rustdoc::merman)]
//! /// Rendered by rustdoc as inline SVG:
//! ///
//! /// ```mermaid
//! /// flowchart TD
//! /// A[Start] --> B[Done]
//! /// ```
//! pub fn example() {}
//! ````
//!
//! # Include Mermaid files
//!
//! Large diagrams can live in separate `.mmd` files. Paths are resolved relative to the consuming
//! crate's `CARGO_MANIFEST_DIR`.
//!
//! ```rust
//! #[cfg_attr(doc, merman_rustdoc::merman)]
//! /// Crate architecture.
//! ///
//! /// include_mmd!("docs/architecture.mmd")
//! pub fn architecture() {}
//! ```
//!
//! # Options
//!
//! The attribute accepts string options:
//!
//! ```rust
//! #[cfg_attr(
//! doc,
//! merman_rustdoc::merman(
//! scope = "item",
//! pipeline = "readable",
//! fail = "error",
//! source = "hide",
//! sanitize = "strict",
//! theme = "rustdoc"
//! )
//! )]
//! /// ```mermaid
//! /// flowchart TD
//! /// A --> B
//! /// ```
//! pub fn configured() {}
//! ```
//!
//! | Option | Values | Default | Meaning |
//! | --- | --- | --- | --- |
//! | `scope` | `item`, `tree` | `item` | Controls whether only the annotated item or the inline item tree is rewritten. |
//! | `pipeline` | `readable`, `parity`, `resvg-safe` | `readable` | Selects the SVG output pipeline. |
//! | `fail` | `error`, `keep-source` | `error` | Controls what happens when rendering or file includes fail. |
//! | `source` | `hide`, `details` | `hide` | Adds a collapsed Mermaid source block under the SVG when set to `details`. |
//! | `sanitize` | `strict`, `off` | `strict` | Checks rendered SVG for script elements, event attributes, and unsafe resource references. |
//! | `theme` | `rustdoc`, `mermaid`, or a supported Mermaid theme name | `rustdoc` | Controls whether diagrams follow rustdoc light/dark themes, use Mermaid source config, or use a fixed Mermaid theme. |
//!
//! Use `scope = "tree"` to process docs on children inside an inline module, trait, impl block,
//! struct fields, and enum variants:
//!
//! ````rust
//! #[cfg_attr(
//! doc,
//! merman_rustdoc::merman(scope = "tree")
//! )]
//! pub mod api {
//! /// ```mermaid
//! /// flowchart TD
//! /// Child --> Docs
//! /// ```
//! pub fn child() {}
//! }
//! ````
//!
//! # Scope
//!
//! Supported today:
//!
//! - Mermaid fences using backticks or tildes.
//! - `include_mmd!("path/to/file.mmd")` lines outside other Markdown code fences.
//! - Item docs on functions, modules, structs, traits, and impl blocks.
//! - Recursive inline item docs with `scope = "tree"`.
//! - Multiple diagrams on the same item.
//! - Footnotes and normal Markdown around diagrams.
//! - Re-exported item docs when the upstream item was rendered first.
//!
//! Not supported today:
//!
//! - Crate-level inner docs using `//!`.
//! - Rewriting Markdown loaded through `#[doc = include_str!("...")]`.
//! - Rustdoc intra-doc symbol links inside rendered Mermaid SVG text.
//! - Recursive processing for external `mod name;` files.
//! - Running Mermaid JavaScript in the browser.
//! - Fetching Mermaid source or assets from remote URLs.
//!
//! # Crate-level docs
//!
//! `merman-rustdoc` rewrites item-level outer docs. It does not rewrite crate-level inner docs
//! written with `//!`.
//!
//! Put crate-level diagrams on a public module or item instead:
//!
//! ````rust
//! #[cfg_attr(doc, merman_rustdoc::merman)]
//! /// Crate architecture.
//! ///
//! /// ```mermaid
//! /// flowchart TD
//! /// Crate --> Module
//! /// ```
//! pub mod architecture {}
//! ````
//!
//! # External docs, links, and themes
//!
//! `merman-rustdoc` does not evaluate or rewrite Markdown loaded through
//! `#[doc = include_str!("...")]`. Use `include_mmd!("path.mmd")` for Mermaid files instead.
//!
//! Mermaid source is rendered to SVG before rustdoc resolves intra-doc links. Text inside the SVG
//! does not participate in rustdoc link resolution, so labels such as `[Type](crate::Type)` are
//! treated as Mermaid text or Mermaid links, not rustdoc symbol links.
//!
//! By default, `merman-rustdoc` follows rustdoc's light/dark theme setting. It renders light and
//! dark SVG variants during `cargo doc` and uses rustdoc's page theme state to show the matching
//! variant.
//! The switch is CSS-only: both variants are embedded in the generated HTML, and the browser does
//! not load Mermaid JavaScript to render or recolor diagrams.
//!
//! Use `theme = "mermaid"` for a single SVG controlled by Mermaid source config. Use
//! `theme = "dark"` or another supported Mermaid theme to choose one fixed build-time theme.
//! Source-level Mermaid config, such as an `%%init%%` directive, is still passed to Merman with the
//! rest of the diagram and overrides the rustdoc-level theme default. Whether a specific theme
//! directive works depends on Merman's renderer support for that diagram and config.
extern crate proc_macro;
use TokenStream;
use TokenStream as TokenStream2;
use quote;
use LitStr;
/// Render Mermaid code fences in rustdoc comments as inline SVG.
///
/// Use this with `cfg_attr` so normal builds do not need to expand diagrams:
///
/// ````rust
/// #[cfg_attr(doc, merman_rustdoc::merman)]
/// /// ```mermaid
/// /// flowchart TD
/// /// A --> B
/// /// ```
/// pub fn example() {}
/// ````