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
//! - Why: To stop defining documentation in multiple places.
//! - How: `user_doc` copies tagged Rust doc comments to runtime-accessible constants.
//!
//! The attribute [user_doc_fn](macro@doc_proc_macro::user_doc_fn) and derive
//! [user_doc_item](macro@doc_proc_macro::user_doc_item) macros
//! capture documentation from comments and make the contents
//! available at runtime.
//!
//! Each macro invocation fills in nodes of a [DocDict](doc_data::DocDict) containing all the documentation
//! captured during a build. At runtime, a global copy of the tree generated at compile time is
//! available at [DOCS](doc_data::DOCS) via a RwLock.
//!
//! ---
//!
//! # The macro options
//! These macros are easily configured with the following helper attributes(for user_doc_item) / arguments (for user_doc_fn):
//!
//! - [chapter_blurb](doc_data::HelperAttr::ChapterBlurb): A string literal that will be added to
//! the containing chapter to describe this documentation.
//! - [chapter_name](doc_data::HelperAttr::ChapterName): A string literal that names this chapter.
//! - [chapter_name_slug](doc_data::HelperAttr::ChapterNameSlug): A comma-separated list of string literals
//! that name a path of chapters.
//! - [chapter_num](doc_data::HelperAttr::ChapterNum): An integer literal corresponding to the number of this chapter.
//! - [chapter_num_slug](doc_data::HelperAttr::ChapterNumSlug): A comma-separated list of integer literals
//! corresponding to a path of chapters.
//!
//! # How to use `user_doc_fn` on a _doc commented_ function definition:
//! Imagine that the documentation for the function `call_this_function` should
//! be visible to the user at runtime.
//! ```ignore
//! #[user_doc_fn(
//! chapter_num_slug(1, 3, 5),
//! chapter_name_slug(
//! "A Slaying in Luton",
//! "The Trouble About Ipswich",
//! "All Along the Weary M-5 Motorway",
//! ),
//! )]
//!
//!
//! /// The parenchyma isn't as stiff as usual. It looks almost floppy.
//! /// I stick out a hand to touch it. It sucks my fingertips forward.
//! /// When I pull my hand back, a hanging bridge of sap follows.
//! pub fn call_this_function() -> bool { true }
//! ```
//!
//! The commented lines (from "The parenchyma" to "sap follows.") will be
//! captured and assigned a location in a tree hierarchy with numbered/named nodes:
//! - 1. "A Slaying in Luton"
//! - 3. "The Trouble About Ipswich"
//! - 5. "All Along the Weary M-5 Motorway"
//!
//! So that at runtime:
//! ```ignore
//! doc_data::load_global_docs(
//! None, None
//! ).expect("must load docs from path");
//! let docs = &*doc_data::DOCS;
//! let docs_read_lock = docs.read().expect("must get read guard on global docs");
//!
//!
//! assert_eq!(
//! docs_read_lock.get_entry_at_numeric_path(
//! &[1,3,5] // corresponds to the `chapter_num_slug` argument in the macro call
//! ).expect("must find test entry").1,
//! " The parenchyma isn\\'t as stiff as usual. It looks almost floppy.\
//! \n I stick out a hand to touch it. It sucks my fingertips forward.\
//! \n When I pull my hand back, a hanging bridge of sap follows.",
//! );
//! ```
//!
//! The doc comment has been inserted in a tree structure. wow. Now, it can be shown to the user
//! at runtime.
//!
//! # How to use `user_doc_item` on a _doc-commented_ struct or enum definition.
//! Imagine the same usage case, but this time, the documentation is attached to struct or enum definition.
//! When working with structs or enums, the outer attribute comments are NOT captured.
//!
//! ```ignore
//! #[derive(user_doc_item, Clone, Debug, PartialEq, Eq)]
//! /// This comment WILL NOT BE captured for user docs.
//! pub enum Idiot {
//! #[chapter_num(23)] // should be overriden by slug
//! #[chapter_num_slug(1, 3, 4)]
//! #[chapter_name("The House of Almond Blossoms")]
//! /// He took a look at the card I showed him. His brows scrunched up like he smelled something offensive.
//! /// Could he tell it was fake? Everyone sweats in heat like this. If they don't, it means they're about to keel over from dehydration anyway. Still, I felt like I was sweating more than usual.
//! /// We stood frozen for a moment. It seemed an eon. Then he mercifully broke the silence.
//! /// "I can't read those. I'm just supposed to stand here and not let anyone pass."
//! /// I considered. I didn't want to get him in trouble – he was clearly new. Still, it was me or him.
//! /// "Oh," I said as casually as I could, "It says you're to let me through. But don't let anyone else through after me. It says that too.""
//! Kid(u16),
//! }
//!
//!
//! let docs = &*user_doc::DOCS;
//! let docs_read_lock = docs.read().expect("must get read guard on global docs");
//! std::println!("docs_read_lock {:?} {:#?}", std::time::Instant::now(), *docs_read_lock );
//! assert_eq!(
//! docs_read_lock.get_entry_at_numeric_path(&
//! [1,3,4], // corresponds to the `chapter_num_slug` helper attribute in the macro call
//! ).expect("must find test entry").0,
//! String::from("The House of Almond Blossoms"),
//! );
//! ```
//! Here, the `chapter_num_slug` helper attribute has overriden the `chapter_num` attribute.
//! Slug-style (number or name) attributes and arguments will always take precedence.
//!
//! # How to prepare a DocDict for use with `mdbook`
//!
//! To expand the global doc store into a hierarchy at the path "tests/scratch/src", do:
//! ```ignore
//! user_doc::load_global_docs(
//! None, None
//! ).expect("must load docs from path");
//! let docs = &*user_doc::DOCS;
//! let docs_read_lock = docs.read().expect("must get read guard on global docs");
//! docs_read_lock.expand_into_mdbook_dirs_at_path(
//! DirectoryNamingScheme::ChapterName,
//! "tests/scratch/src",
//! ).expect("must expand docs into dirs");
//! ```
//! The directory `tests/scratch/src` will be filled with documentation corresponding to
//! [mdbook](https://crates.io/crates/mdbook) format.
//!
//!
//! # How to use in a workspace project
//!
//! When capturing documentation data in a workspace with multiple sub-projects,
//! there's only a single instance of the global documentation capture store.
//! As such, documents captured from the last sub-project that gets compiled
//! tend to overwrite those from those previously captured.
//!
//! ## Workaround
//! Just specify a different file name for each capture using the
//! `set_persistence_file_name` macro. Then, supply said file name
//! to the `load_global_docs` function call at runtime.
//!
//! Given a project tree that looks something like:
//! ```ignore
//! some_workspace
//! └ sub_package_a
//! ┕ src/lib.rs
//! └ sub_package_b
//! ┕ src/lib.rs
//! ```
//!
//! 1. Invoke `set_persistence_file_name("docs_a_or_whatever")` in
//! `sub_package_a/src/lib.rs`.
//! 2. Invoke `set_persistence_file_name("docs_b_or_whatever")` in
//! `sub_package_b/src/lib.rs`
//! 3. Elsewhere, to load docs from both packages into new dictionaries:
//! ```ignore
//! let mut dict_a = DocDict::default();
//! load_global_docs("docs_a_or_whatever", Some(&mut dict_a));
//! let mut dict_b = DocDict::default();
//! load_global_docs("docs_b_or_whatever", Some(&mut dict_b));
//! ```
//! # Critical Note
//! These macros use a temporary directory to persist data from compile-time to runtime.
//! Do not store sensitive information (keys, passwords, etc.) in doc comments captured with these macros.
extern crate doc_data;
pub use *;
extern crate doc_proc_macro;
pub use *;