Skip to content

Commit c398d4b

Browse files
committed
allow Node::walk_* functions to terminate early
This commit adds a Result<> to all walk callbacks. In order to use this, you should change all `Node::walk_*` methods in your code the following way: ```rust // replace this: node.walk(|node, _| { dbg!(node); }); // with this (unwrap is safe here because walk only // returns error when your function does): node.walk(|node, _| { dbg!(node); Ok(()) }).unwrap(); ```
1 parent d2b8cd4 commit c398d4b

File tree

17 files changed

+131
-51
lines changed

17 files changed

+131
-51
lines changed

CHANGELOG.md

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,37 @@
11
# Changelog
22

3+
## 0.6.0 - WIP
4+
5+
### Added
6+
7+
- added `md.try_parse()` function which may return an error, as opposed to existing
8+
`md.parse()` function which never does
9+
10+
- added optional `try_run()` trait function for rules which can fail and will
11+
propagate errors when using `md.try_parse()`
12+
13+
### Changed
14+
15+
- `Node::walk_*` methods now return `Result`, which allows you to terminate traversing early
16+
17+
### Migration
18+
19+
For all `Node::walk_*` methods change the following:
20+
21+
```rust
22+
// replace this:
23+
node.walk(|node, _| {
24+
dbg!(node);
25+
});
26+
27+
// with this (unwrap is safe here because walk only
28+
// returns error when your function does):
29+
node.walk(|node, _| {
30+
dbg!(node);
31+
Ok(())
32+
}).unwrap();
33+
```
34+
335
## 0.5.0 - 2023-05-13
436

537
### Added

examples/ferris/core_rule.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,8 @@ impl CoreRule for FerrisCounterRule {
5151
if node.is::<InlineFerris>() || node.is::<BlockFerris>() {
5252
counter += 1;
5353
}
54-
});
54+
Ok(())
55+
}).unwrap();
5556

5657
// append a counter to the root as a custom node
5758
root.children.push(Node::new(FerrisCounter(counter)))

src/bin.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,8 @@ fn main() {
9393
} else {
9494
println!("{name}");
9595
}
96-
});
96+
Ok(())
97+
}).unwrap();
9798
return;
9899
}
99100

src/generics/inline/emph_pair.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -265,7 +265,10 @@ fn is_odd_match(opener: &EmphMarker, closer: &EmphMarker) -> bool {
265265
pub struct FragmentsJoin;
266266
impl CoreRule for FragmentsJoin {
267267
fn run(node: &mut Node, _: &MarkdownIt) {
268-
node.walk_mut(|node, _| fragments_join(node));
268+
node.walk_mut(|node, _| {
269+
fragments_join(node);
270+
Ok(())
271+
}).unwrap();
269272
}
270273
}
271274

src/parser/node.rs

Lines changed: 49 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use crate::common::TypeKey;
77
use crate::parser::extset::NodeExtSet;
88
use crate::parser::inline::Text;
99
use crate::parser::renderer::HTMLRenderer;
10-
use crate::Renderer;
10+
use crate::{Renderer, Result};
1111

1212
/// Single node in the CommonMark AST.
1313
#[derive(Debug)]
@@ -106,76 +106,91 @@ impl Node {
106106

107107
/// Execute function `f` recursively on every member of AST tree
108108
/// (using preorder deep-first search).
109-
pub fn walk<'a>(&'a self, mut f: impl FnMut(&'a Node, u32)) {
109+
///
110+
/// This function will stop early if closure returns any error.
111+
pub fn walk<'a>(&'a self, mut f: impl FnMut(&'a Node, u32) -> Result<()>) -> Result<()> {
110112
// performance note: this is faster than emulating recursion using vec stack
111-
fn walk_recursive<'b>(node: &'b Node, depth: u32, f: &mut impl FnMut(&'b Node, u32)) {
112-
f(node, depth);
113+
fn walk_recursive<'a>(node: &'a Node, depth: u32, f: &mut impl FnMut(&'a Node, u32) -> Result<()>) -> Result<()> {
114+
f(node, depth)?;
113115
for n in node.children.iter() {
114-
stacker::maybe_grow(64*1024, 1024*1024, || {
115-
walk_recursive(n, depth + 1, f);
116-
});
116+
stacker::maybe_grow(64*1024, 1024*1024, || -> Result<()> {
117+
walk_recursive(n, depth + 1, f)
118+
})?;
117119
}
120+
Ok(())
118121
}
119122

120-
walk_recursive(self, 0, &mut f);
123+
walk_recursive(self, 0, &mut f)
121124
}
122125

123126
/// Execute function `f` recursively on every member of AST tree
124127
/// (using preorder deep-first search).
125-
pub fn walk_mut(&mut self, mut f: impl FnMut(&mut Node, u32)) {
126-
// performance note: this is faster than emulating recursion using vec stack
127-
fn walk_recursive(node: &mut Node, depth: u32, f: &mut impl FnMut(&mut Node, u32)) {
128-
f(node, depth);
128+
///
129+
/// This function will stop early if closure returns any error.
130+
pub fn walk_mut(&mut self, mut f: impl FnMut(&mut Node, u32) -> Result<()>) -> Result<()> {
131+
// note: lifetime constrains are different from non-mut walk due to mutability
132+
fn walk_recursive(node: &mut Node, depth: u32, f: &mut impl FnMut(&mut Node, u32) -> Result<()>) -> Result<()> {
133+
f(node, depth)?;
129134
for n in node.children.iter_mut() {
130-
stacker::maybe_grow(64*1024, 1024*1024, || {
131-
walk_recursive(n, depth + 1, f);
132-
});
135+
stacker::maybe_grow(64*1024, 1024*1024, || -> Result<()> {
136+
walk_recursive(n, depth + 1, f)
137+
})?;
133138
}
139+
Ok(())
134140
}
135141

136-
walk_recursive(self, 0, &mut f);
142+
walk_recursive(self, 0, &mut f)
137143
}
138144

139145
/// Execute function `f` recursively on every member of AST tree
140146
/// (using postorder deep-first search).
141-
pub fn walk_post(&self, mut f: impl FnMut(&Node, u32)) {
142-
fn walk_recursive(node: &Node, depth: u32, f: &mut impl FnMut(&Node, u32)) {
147+
///
148+
/// This function will stop early if closure returns any error.
149+
pub fn walk_post<'a>(&'a self, mut f: impl FnMut(&'a Node, u32) -> Result<()>) -> Result<()> {
150+
// performance note: this is faster than emulating recursion using vec stack
151+
fn walk_recursive<'a>(node: &'a Node, depth: u32, f: &mut impl FnMut(&'a Node, u32) -> Result<()>) -> Result<()> {
143152
for n in node.children.iter() {
144-
stacker::maybe_grow(64*1024, 1024*1024, || {
145-
walk_recursive(n, depth + 1, f);
146-
});
153+
stacker::maybe_grow(64*1024, 1024*1024, || -> Result<()> {
154+
walk_recursive(n, depth + 1, f)
155+
})?;
147156
}
148-
f(node, depth);
157+
f(node, depth)?;
158+
Ok(())
149159
}
150160

151-
walk_recursive(self, 0, &mut f);
161+
walk_recursive(self, 0, &mut f)
152162
}
153163

154164
/// Execute function `f` recursively on every member of AST tree
155165
/// (using postorder deep-first search).
156-
pub fn walk_post_mut(&mut self, mut f: impl FnMut(&mut Node, u32)) {
157-
fn walk_recursive(node: &mut Node, depth: u32, f: &mut impl FnMut(&mut Node, u32)) {
166+
///
167+
/// This function will stop early if closure returns any error.
168+
pub fn walk_post_mut(&mut self, mut f: impl FnMut(&mut Node, u32) -> Result<()>) -> Result<()> {
169+
// note: lifetime constrains are different from non-mut walk due to mutability
170+
fn walk_recursive(node: &mut Node, depth: u32, f: &mut impl FnMut(&mut Node, u32) -> Result<()>) -> Result<()> {
158171
for n in node.children.iter_mut() {
159-
stacker::maybe_grow(64*1024, 1024*1024, || {
160-
walk_recursive(n, depth + 1, f);
161-
});
172+
stacker::maybe_grow(64*1024, 1024*1024, || -> Result<()> {
173+
walk_recursive(n, depth + 1, f)
174+
})?;
162175
}
163-
f(node, depth);
176+
f(node, depth)?;
177+
Ok(())
164178
}
165179

166-
walk_recursive(self, 0, &mut f);
180+
walk_recursive(self, 0, &mut f)
167181
}
168182

169183
/// Walk recursively through child nodes and collect all text nodes
170184
/// into a single string.
171185
pub fn collect_text(&self) -> String {
172186
let mut result = String::new();
173187

174-
self.walk(|node, _| {
188+
self.walk(|node: &Node, _| {
175189
if let Some(text) = node.cast::<Text>() {
176190
result.push_str(text.content.as_str());
177191
}
178-
});
192+
Ok(())
193+
}).unwrap();
179194

180195
result
181196
}
@@ -185,7 +200,8 @@ impl Drop for Node {
185200
fn drop(&mut self) {
186201
self.walk_post_mut(|node, _| {
187202
drop(std::mem::take(&mut node.children));
188-
});
203+
Ok(())
204+
}).unwrap();
189205
}
190206
}
191207

src/plugins/extra/heading_anchors.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ impl CoreRule for AddHeadingAnchors {
6464
if node.is::<ATXHeading>() || node.is::<SetextHeader>() {
6565
node.attrs.push(("id", slugify(&node.collect_text())));
6666
}
67-
});
67+
Ok(())
68+
}).unwrap();
6869
}
6970
}

src/plugins/extra/smartquotes.rs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,8 @@ impl<
153153
text_node.content = execute_replacements(current_replacements, &text_node.content);
154154
};
155155
current_index += 1;
156-
});
156+
Ok(())
157+
}).unwrap();
157158
}
158159
}
159160

@@ -318,7 +319,7 @@ impl<
318319
fn all_text_tokens(root: &Node) -> Vec<FlatToken> {
319320
let mut result = Vec::new();
320321
let mut walk_index = 0;
321-
root.walk(|node, nesting_level| {
322+
root.walk(|node: &Node, nesting_level| {
322323
if let Some(text_node) = node.cast::<Text>() {
323324
result.push(FlatToken::Text {
324325
content: &text_node.content,
@@ -335,7 +336,8 @@ fn all_text_tokens(root: &Node) -> Vec<FlatToken> {
335336
result.push(FlatToken::Irrelevant);
336337
}
337338
walk_index += 1;
338-
});
339+
Ok(())
340+
}).unwrap();
339341
result
340342
}
341343

src/plugins/extra/syntect.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ impl CoreRule for SyntectRule {
6969
node.replace(SyntectSnippet { html });
7070
}
7171
}
72-
});
72+
Ok(())
73+
}).unwrap();
7374
}
7475
}

src/plugins/extra/typographer.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ pub struct TypographerRule;
8282
impl CoreRule for TypographerRule {
8383
fn run(root: &mut Node, _: &MarkdownIt) {
8484
root.walk_mut(|node, _| {
85-
let Some(mut text_node) = node.cast_mut::<Text>() else { return; };
85+
let Some(mut text_node) = node.cast_mut::<Text>() else { return Ok(()); };
8686

8787
if SCOPED_RE.is_match(&text_node.content) {
8888
text_node.content = SCOPED_RE
@@ -119,6 +119,7 @@ impl CoreRule for TypographerRule {
119119
text_node.content = s;
120120
}
121121
}
122-
});
122+
Ok(())
123+
}).unwrap();
123124
}
124125
}

src/plugins/sourcepos.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,8 @@ impl CoreRule for SyntaxPosRule {
3535
let ((startline, startcol), (endline, endcol)) = map.get_positions(&mapping);
3636
node.attrs.push(("data-sourcepos", format!("{}:{}-{}:{}", startline, startcol, endline, endcol)));
3737
}
38-
});
38+
Ok(())
39+
}).unwrap();
3940
Ok(())
4041
}
4142

0 commit comments

Comments
 (0)