11//! Handling for rule identifiers.
22
33use crate :: Spec ;
4- use mdbook:: book:: Chapter ;
4+ use mdbook:: book:: Book ;
5+ use mdbook:: BookItem ;
56use once_cell:: sync:: Lazy ;
67use regex:: { Captures , Regex } ;
78use std:: collections:: BTreeMap ;
@@ -10,33 +11,65 @@ use std::path::PathBuf;
1011/// The Regex for rules like `r[foo]`.
1112static RULE_RE : Lazy < Regex > = Lazy :: new ( || Regex :: new ( r"(?m)^r\[([^]]+)]$" ) . unwrap ( ) ) ;
1213
14+ /// The set of rules defined in the reference.
15+ #[ derive( Default ) ]
16+ pub struct Rules {
17+ /// A mapping from a rule identifier to a tuple of `(source_path, path)`.
18+ ///
19+ /// `source_path` is the path to the markdown source file relative to the
20+ /// `SUMMARY.md`.
21+ ///
22+ /// `path` is the same as `source_path`, except filenames like `README.md`
23+ /// are translated to `index.md`. Which to use depends on if you are
24+ /// trying to access the source files (`source_path`), or creating links
25+ /// in the output (`path`).
26+ pub def_paths : BTreeMap < String , ( PathBuf , PathBuf ) > ,
27+ }
28+
1329impl Spec {
30+ /// Collects all rule definitions in the book.
31+ pub fn collect_rules ( & self , book : & Book ) -> Rules {
32+ let mut rules = Rules :: default ( ) ;
33+ for item in book. iter ( ) {
34+ let BookItem :: Chapter ( ch) = item else {
35+ continue ;
36+ } ;
37+ if ch. is_draft_chapter ( ) {
38+ continue ;
39+ }
40+ RULE_RE
41+ . captures_iter ( & ch. content )
42+ . for_each ( |caps : Captures < ' _ > | {
43+ let rule_id = & caps[ 1 ] ;
44+ let source_path = ch. source_path . clone ( ) . unwrap_or_default ( ) ;
45+ let path = ch. path . clone ( ) . unwrap_or_default ( ) ;
46+ if let Some ( ( old, _) ) = rules
47+ . def_paths
48+ . insert ( rule_id. to_string ( ) , ( source_path. clone ( ) , path. clone ( ) ) )
49+ {
50+ let message = format ! (
51+ "rule `{rule_id}` defined multiple times\n \
52+ First location: {old:?}\n \
53+ Second location: {source_path:?}"
54+ ) ;
55+ if self . deny_warnings {
56+ panic ! ( "error: {message}" ) ;
57+ } else {
58+ eprintln ! ( "warning: {message}" ) ;
59+ }
60+ }
61+ } ) ;
62+ }
63+
64+ rules
65+ }
66+
1467 /// Converts lines that start with `r[…]` into a "rule" which has special
1568 /// styling and can be linked to.
16- pub fn rule_definitions (
17- & self ,
18- chapter : & Chapter ,
19- found_rules : & mut BTreeMap < String , ( PathBuf , PathBuf ) > ,
20- ) -> String {
21- let source_path = chapter. source_path . clone ( ) . unwrap_or_default ( ) ;
22- let path = chapter. path . clone ( ) . unwrap_or_default ( ) ;
69+ pub fn render_rule_definitions ( & self , content : & str ) -> String {
2370 RULE_RE
24- . replace_all ( & chapter . content , |caps : & Captures < ' _ > | {
71+ . replace_all ( content, |caps : & Captures < ' _ > | {
2572 let rule_id = & caps[ 1 ] ;
26- if let Some ( ( old, _) ) =
27- found_rules. insert ( rule_id. to_string ( ) , ( source_path. clone ( ) , path. clone ( ) ) )
28- {
29- let message = format ! (
30- "rule `{rule_id}` defined multiple times\n \
31- First location: {old:?}\n \
32- Second location: {source_path:?}"
33- ) ;
34- if self . deny_warnings {
35- panic ! ( "error: {message}" ) ;
36- } else {
37- eprintln ! ( "warning: {message}" ) ;
38- }
39- }
4073 format ! (
4174 "<div class=\" rule\" id=\" r-{rule_id}\" >\
4275 <a class=\" rule-link\" href=\" #r-{rule_id}\" >[{rule_id}]</a>\
0 commit comments