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
//! Aquamarine is a procedural macro extension for [rustdoc](https://doc.rust-lang.org/rustdoc/what-is-rustdoc.html),
//! that aims to improve the visual component of Rust documentation through use of the [mermaid.js](https://mermaid-js.github.io/mermaid/#/) diagrams.
//!
//! `#[aquamarine]` macro works through embedding the [mermaid.js](https://github.com/mermaid-js/mermaid) into the generated rustdoc HTML page, modifying the doc comment attributes.
//!
//! To inline a diagram into the documentation, use the `mermaid` snippet in a doc-string:
//!
//! ```rust
//! # use aquamarine::aquamarine;
//! #[cfg_attr(doc, aquamarine)]
//! /// ```mermaid
//! /// graph LR
//! /// s([Source]) --> a[[aquamarine]]
//! /// r[[rustdoc]] --> f([Docs w/ Mermaid!])
//! /// subgraph rustc[Rust Compiler]
//! /// a -. inject mermaid.js .-> r
//! /// end
//! /// ```
//! pub fn example() {}
//! ```
//! The diagram will appear in place of the `mermaid` code block, preserving all the comments around it.
//!
//! You can even add multiple diagrams!
//!
//! To see it in action, go to the [demo crate](https://docs.rs/aquamarine-demo-crate/0.5.0/aquamarine_demo_crate/fn.example.html) docs.rs page.
//!
//! ### Dark-mode
//!
//! Aquamarine will automatically select the `dark` theme as a default, if the current `rustdoc` theme is either `ayu` or `dark`.
//!
//! You might need to reload the page to redraw the diagrams after changing the theme.
//!
//! ### Custom themes
//!
//! Theming is supported on per-diagram basis, through the mermaid's `%%init%%` attribute.
//!
//! *Note*: custom theme will override the default theme
//!
//! ```no_run
//! /// ```mermaid
//! /// %%{init: {
//! /// 'theme': 'base',
//! /// 'themeVariables': {
//! /// 'primaryColor': '#ffcccc',
//! /// 'edgeLabelBackground':'#ccccff',
//! /// 'tertiaryColor': '#fff0f0' }}}%%
//! /// graph TD
//! /// A(Diagram needs to be drawn) --> B{Does it have 'init' annotation?}
//! /// B -->|No| C(Apply default theme)
//! /// B -->|Yes| D(Apply customized theme)
//! /// ```
//! # fn example() {}
//! ```
//! [Demo on docs.rs](https://docs.rs/aquamarine-demo-crate/0.5.0/aquamarine_demo_crate/fn.example_with_styling.html)
//!
//! To learn more, see the [Theming Section](https://mermaid-js.github.io/mermaid/#/theming) of the mermaid.js book
//!
//! ### Loading from a file
//!
//! When describing complex logic, a diagram can get quite big.
//!
//! To reduce clutter in your doc comments, you can load a diagram from file using the `include_mmd!` macro-like syntax.
//!
//! ```no_run
//! /// Diagram #1
//! /// include_mmd!("diagram_1.mmd")
//! ///
//! /// Diagram #2
//! /// include_mmd!("diagram_2.mmd")
//! # fn example() {}
//! ```
//! [Demo on docs.rs](https://docs.rs/aquamarine-demo-crate/0.5.0/aquamarine_demo_crate/fn.example_load_from_file.html)
extern crate proc_macro;
use proc_macro::TokenStream;
use proc_macro_error::{abort, proc_macro_error};
use quote::quote;
use syn::{parse_macro_input, Attribute};
mod attrs;
mod parse;
/// Aquamarine is a proc-macro that adds [Mermaid](https://mermaid-js.github.io/mermaid/#/) diagrams to rustdoc
///
/// To inline a diagram into the documentation, use the `mermaid` snippet:
///
/// ```rust
/// # use aquamarine::aquamarine;
/// #[aquamarine]
/// /// ```mermaid
/// /// --- here goes your mermaid diagram ---
/// /// ```
/// struct Foo;
/// ```
#[proc_macro_attribute]
#[proc_macro_error]
pub fn aquamarine(_args: TokenStream, input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as parse::Input);
check_input_attrs(&input.attrs);
let attrs = attrs::Attrs::from(input.attrs);
let forward = input.rest;
let tokens = quote! {
#attrs
#forward
};
tokens.into()
}
fn check_input_attrs(input: &[Attribute]) {
for attr in input {
if attr.path().is_ident("aquamarine") {
abort!(
attr,
"multiple `aquamarine` attributes on one entity are illegal"
);
}
}
}