From 57e00b826093e0569b391d17eee2827f2670e43a Mon Sep 17 00:00:00 2001 From: Kae Sluder Date: Fri, 26 Apr 2024 22:58:02 -0400 Subject: [PATCH 1/3] replace AST example on README.md Updated the AST example on README.md to use AstNode.descendants() as convenience iterator. 1. Using the iterator should be sufficient for most cases and is hopefully more familiar to people with experience at DOM/AST manipulation in other languages. 2. Full example in examples/iterator_replace.rs. It should be possible to drop the whole block from README into a file and run the demo. 3. Ref and lifetime allocation for this example is encapsulated in the Arena and AstNode methods. --- README.md | 65 ++++++++++++++++-------------------- examples/iterator_replace.rs | 33 ++++++++++++++++++ 2 files changed, 62 insertions(+), 36 deletions(-) create mode 100644 examples/iterator_replace.rs diff --git a/README.md b/README.md index 9a38ce09..a9ac0a62 100644 --- a/README.md +++ b/README.md @@ -162,45 +162,38 @@ Or you can parse the input into an AST yourself, manipulate it, and then use you ``` rust extern crate comrak; -use comrak::{parse_document, format_html, Arena, Options}; -use comrak::nodes::{AstNode, NodeValue}; - -// The returned nodes are created in the supplied Arena, and are bound by its lifetime. -let arena = Arena::new(); - -let root = parse_document( - &arena, - "This is my input.\n\n1. Also my input.\n2. Certainly my input.\n", - &Options::default()); - -fn iter_nodes<'a, F>(node: &'a AstNode<'a>, f: &F) - where F : Fn(&'a AstNode<'a>) { - f(node); - for c in node.children() { - iter_nodes(c, f); - } -} +use comrak::nodes::NodeValue; +use comrak::{format_html, parse_document, Arena, Options}; + +fn replace_text(document: &str, orig_string: &str, replacement: &str) -> String { + // The returned nodes are created in the supplied Arena, and are bound by its lifetime. + let arena = Arena::new(); -iter_nodes(root, &|node| { - match &mut node.data.borrow_mut().value { - &mut NodeValue::Text(ref mut text) => { - let orig = std::mem::replace(text, vec![]); - *text = String::from_utf8(orig).unwrap().replace("my", "your").as_bytes().to_vec(); + // Parse the document into a root `AstNode` + let root = parse_document(&arena, document, &Options::default()); + + // Iterate over all the descendants of root. + for node in root.descendants() { + if let NodeValue::Text(ref mut text) = node.data.borrow_mut().value { + // If the node is a text node, append its text to `output_text`. + *text = text.replace(orig_string, replacement) } - _ => (), } -}); - -let mut html = vec![]; -format_html(root, &Options::default(), &mut html).unwrap(); - -assert_eq!( - String::from_utf8(html).unwrap(), - "

This is your input.

\n\ -
    \n\ -
  1. Also your input.
  2. \n\ -
  3. Certainly your input.
  4. \n\ -
\n"); + + let mut html = vec![]; + format_html(root, &Options::default(), &mut html).unwrap(); + + String::from_utf8(html).unwrap() +} + +fn main() { + let doc = "This is my input.\n\n1. Also [my](#) input.\n2. Certainly *my* input.\n".to_string(); + let orig = "my".to_string(); + let repl = "your".to_string(); + let html = replace_text(&doc, &orig, &repl); + + println!("{}", html); +} ``` ## Benchmarking diff --git a/examples/iterator_replace.rs b/examples/iterator_replace.rs new file mode 100644 index 00000000..82f44525 --- /dev/null +++ b/examples/iterator_replace.rs @@ -0,0 +1,33 @@ +extern crate comrak; +use comrak::nodes::NodeValue; +use comrak::{format_html, parse_document, Arena, Options}; + +fn replace_text(document: &str, orig_string: &str, replacement: &str) -> String { + // The returned nodes are created in the supplied Arena, and are bound by its lifetime. + let arena = Arena::new(); + + // Parse the document into a root `AstNode` + let root = parse_document(&arena, document, &Options::default()); + + // Iterate over all the descendants of root. + for node in root.descendants() { + if let NodeValue::Text(ref mut text) = node.data.borrow_mut().value { + // If the node is a text node, append its text to `output_text`. + *text = text.replace(orig_string, replacement) + } + } + + let mut html = vec![]; + format_html(root, &Options::default(), &mut html).unwrap(); + + String::from_utf8(html).unwrap() +} + +fn main() { + let doc = "This is my input.\n\n1. Also [my](#) input.\n2. Certainly *my* input.\n".to_string(); + let orig = "my".to_string(); + let repl = "your".to_string(); + let html = replace_text(&doc, &orig, &repl); + + println!("{}", html); +} From a99c47e6e78e014e28604465964df6f76b89780f Mon Sep 17 00:00:00 2001 From: kaesluder <113563866+kaesluder@users.noreply.github.com> Date: Fri, 26 Apr 2024 23:17:16 -0400 Subject: [PATCH 2/3] update comment --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index a9ac0a62..0b33417b 100644 --- a/README.md +++ b/README.md @@ -175,7 +175,7 @@ fn replace_text(document: &str, orig_string: &str, replacement: &str) -> String // Iterate over all the descendants of root. for node in root.descendants() { if let NodeValue::Text(ref mut text) = node.data.borrow_mut().value { - // If the node is a text node, append its text to `output_text`. + // If the node is a text node, perform the string replacement. *text = text.replace(orig_string, replacement) } } From 21dbe45bcd4c138a637efda69a458187317f50c6 Mon Sep 17 00:00:00 2001 From: Kae Sluder Date: Sat, 27 Apr 2024 10:57:05 -0400 Subject: [PATCH 3/3] check comments, add test, remove unnecessry .to_string() 1. Check comments for accuracy 2. Add basic unit test. Test checks output for string replacement and use of key html tags. Not sensitive to whitespace handling. 3. Remove unnecessary to_string() calls from main() and test function. --- README.md | 6 +++--- examples/iterator_replace.rs | 27 +++++++++++++++++++++++---- 2 files changed, 26 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 0b33417b..313eac90 100644 --- a/README.md +++ b/README.md @@ -187,9 +187,9 @@ fn replace_text(document: &str, orig_string: &str, replacement: &str) -> String } fn main() { - let doc = "This is my input.\n\n1. Also [my](#) input.\n2. Certainly *my* input.\n".to_string(); - let orig = "my".to_string(); - let repl = "your".to_string(); + let doc = "This is my input.\n\n1. Also [my](#) input.\n2. Certainly *my* input.\n"; + let orig = "my"; + let repl = "your"; let html = replace_text(&doc, &orig, &repl); println!("{}", html); diff --git a/examples/iterator_replace.rs b/examples/iterator_replace.rs index 82f44525..3fbef1fc 100644 --- a/examples/iterator_replace.rs +++ b/examples/iterator_replace.rs @@ -1,6 +1,7 @@ extern crate comrak; use comrak::nodes::NodeValue; use comrak::{format_html, parse_document, Arena, Options}; +use ntest::{assert_false, assert_true}; fn replace_text(document: &str, orig_string: &str, replacement: &str) -> String { // The returned nodes are created in the supplied Arena, and are bound by its lifetime. @@ -12,7 +13,7 @@ fn replace_text(document: &str, orig_string: &str, replacement: &str) -> String // Iterate over all the descendants of root. for node in root.descendants() { if let NodeValue::Text(ref mut text) = node.data.borrow_mut().value { - // If the node is a text node, append its text to `output_text`. + // If the node is a text node, replace `orig_string` with `replacement`. *text = text.replace(orig_string, replacement) } } @@ -24,10 +25,28 @@ fn replace_text(document: &str, orig_string: &str, replacement: &str) -> String } fn main() { - let doc = "This is my input.\n\n1. Also [my](#) input.\n2. Certainly *my* input.\n".to_string(); - let orig = "my".to_string(); - let repl = "your".to_string(); + let doc = "This is my input.\n\n1. Also [my](#) input.\n2. Certainly *my* input.\n"; + let orig = "my"; + let repl = "your"; let html = replace_text(&doc, &orig, &repl); println!("{}", html); } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn sample_replace() { + let doc = "Replace deeply nested *[foo](https://example.com)* with bar.\n\nReplace shallow foo with bar."; + let orig = "foo"; + let repl = "bar"; + let html = replace_text(&doc, &orig, &repl); + println!("{:?}", html); + assert_false!(html.contains("foo")); + assert_true!(html.contains("bar")); + assert_true!(html.contains("