Skip to content

Commit 160bb6a

Browse files
committed
Move rule collection into a separate pass with a new structure
This adds a "Rules" struct which encapsulates the rules in the document. This also removes some of the mutable variables. This is paving the way for future updates which extends information and processing about rules.
1 parent f99b45a commit 160bb6a

File tree

2 files changed

+65
-44
lines changed

2 files changed

+65
-44
lines changed

mdbook-spec/src/lib.rs

Lines changed: 9 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#![deny(rust_2018_idioms, unused_lifetimes)]
22

3+
use crate::rules::Rules;
34
use anyhow::Result;
45
use mdbook::book::{Book, Chapter};
56
use mdbook::errors::Error;
@@ -8,9 +9,7 @@ use mdbook::BookItem;
89
use once_cell::sync::Lazy;
910
use regex::{Captures, Regex};
1011
use semver::{Version, VersionReq};
11-
use std::collections::BTreeMap;
1212
use std::io;
13-
use std::path::PathBuf;
1413

1514
mod rules;
1615
mod std_links;
@@ -58,13 +57,10 @@ impl Spec {
5857

5958
/// Generates link references to all rules on all pages, so you can easily
6059
/// refer to rules anywhere in the book.
61-
fn auto_link_references(
62-
&self,
63-
chapter: &Chapter,
64-
found_rules: &BTreeMap<String, (PathBuf, PathBuf)>,
65-
) -> String {
60+
fn auto_link_references(&self, chapter: &Chapter, rules: &Rules) -> String {
6661
let current_path = chapter.path.as_ref().unwrap().parent().unwrap();
67-
let definitions: String = found_rules
62+
let definitions: String = rules
63+
.def_paths
6864
.iter()
6965
.map(|(rule_id, (_, path))| {
7066
let relative = pathdiff::diff_paths(path, current_path).unwrap();
@@ -125,28 +121,20 @@ impl Preprocessor for Spec {
125121
}
126122

127123
fn run(&self, _ctx: &PreprocessorContext, mut book: Book) -> Result<Book, Error> {
128-
let mut found_rules = BTreeMap::new();
124+
let rules = self.collect_rules(&book);
125+
129126
book.for_each_mut(|item| {
130127
let BookItem::Chapter(ch) = item else {
131128
return;
132129
};
133130
if ch.is_draft_chapter() {
134131
return;
135132
}
136-
ch.content = self.rule_definitions(&ch, &mut found_rules);
137133
ch.content = self.admonitions(&ch);
134+
ch.content = self.auto_link_references(&ch, &rules);
135+
ch.content = self.render_rule_definitions(&ch.content);
138136
});
139-
// This is a separate pass because it relies on the modifications of
140-
// the previous passes.
141-
book.for_each_mut(|item| {
142-
let BookItem::Chapter(ch) = item else {
143-
return;
144-
};
145-
if ch.is_draft_chapter() {
146-
return;
147-
}
148-
ch.content = self.auto_link_references(&ch, &found_rules);
149-
});
137+
150138
// Final pass will resolve everything as a std link (or error if the
151139
// link is unknown).
152140
std_links::std_links(&mut book);

mdbook-spec/src/rules.rs

Lines changed: 56 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
//! Handling for rule identifiers.
22
33
use crate::Spec;
4-
use mdbook::book::Chapter;
4+
use mdbook::book::Book;
5+
use mdbook::BookItem;
56
use once_cell::sync::Lazy;
67
use regex::{Captures, Regex};
78
use std::collections::BTreeMap;
@@ -10,33 +11,65 @@ use std::path::PathBuf;
1011
/// The Regex for rules like `r[foo]`.
1112
static 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+
1329
impl 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

Comments
 (0)