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
//! An ergonomic library for manipulating markdown documents.
//!
//! There are two fundamental concepts in `markedit`,
//!
//! - `Matcher` - something which can match an [`Event`] from pulldown-cmark
//!   (typically implemented using state machines or simple functions)
//! - `Rewriter` - something which can rewrite part of a stream of [`Event`]s
//!   (typically just a function)
//!
//! Together we can use these to transform a stream of [`Event`]s on the fly
//! with minimal overhead.
//!
//! # Examples
//!
//! The use case which prompted this entire library was to insert arbitrary
//! markdown after a heading.
//!
//! ```rust
//! use pulldown_cmark::{Event, Tag};
//! use markedit::{Matcher, Heading};
//!
//! let src = "# Heading\n Some text\n some more text \n\n # Another Heading";
//!
//! // first we need to construct our predicate
//! let matcher = Heading::with_level(1).falling_edge();
//!
//! // we also need a rewriting rule
//! let rule = markedit::insert_markdown_before("## Sub-Heading", matcher);
//!
//! // create our stream of events
//! let events = markedit::parse(src);
//! // then mutate them and collect them into a vector so we can inspect the
//! // results
//! let mutated: Vec<_> = markedit::rewrite(events, rule).collect();
//!
//! // the heading before we want to insert
//! assert_eq!(mutated[1], Event::Text("Heading".into()));
//! // our inserted tags
//! assert_eq!(mutated[3], Event::Start(Tag::Heading(2)));
//! assert_eq!(mutated[4], Event::Text("Sub-Heading".into()));
//! assert_eq!(mutated[5], Event::End(Tag::Heading(2)));
//! // "Some text" is the line after
//! assert_eq!(mutated[7], Event::Text("Some text".into()));
//! ```
//!
//! You can also use [`change_text()`] to upper-case text based on a predicate
//! (e.g. the text contains a certain keyword).
//!
//! ```rust
//! use pulldown_cmark::Event;
//!
//! let src = "# Heading\n Some text \n some more text \n\n # Another Heading";
//!
//! // first we construct the rewriting rule
//! let rule = markedit::change_text(
//!     |text| text.contains("Heading"),
//!     |text| text.to_uppercase(),
//! );
//!
//! // let's parse the input text into Events
//! let events_before: Vec<_> = markedit::parse(src).collect();
//!
//! // some sanity checks on the initial input
//! assert_eq!(events_before[1], Event::Text("Heading".into()));
//! assert_eq!(events_before[9], Event::Text("Another Heading".into()));
//!
//! // now rewrite the events using our rewriter rule
//! let events_after: Vec<_> = markedit::rewrite(events_before, rule)
//!     .collect();
//!
//! // and check the results
//! println!("{:?}", events_after);
//! assert_eq!(events_after[1], Event::Text("HEADING".into()));
//! assert_eq!(events_after[9], Event::Text("ANOTHER HEADING".into()));
//! ```
//!
//! Note that everything works with streaming iterators, we only needed to
//! `collect()` the events into a `Vec` for demonstration purposes.

#![forbid(unsafe_code)]
#![deny(missing_docs, missing_debug_implementations, rust_2018_idioms)]

pub use pulldown_cmark;

mod matchers;
mod rewriters;

pub use matchers::*;
pub use rewriters::*;

use pulldown_cmark::{Event, Parser};

/// A convenience function for parsing some text into [`Event`]s without
/// needing to add [`pulldown_cmark`] as an explicit dependency.
pub fn parse(text: &str) -> impl Iterator<Item = Event<'_>> + '_ {
    Parser::new(text)
}