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 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286
mod after; mod before; mod bottom; mod head; mod link; mod parser; mod raw; mod share; mod tail; mod within; mod iterwith; pub use after::After; pub use before::Before; pub use bottom::Bottom; pub use head::Head; pub use link::Link; pub use parser::Parser; pub use raw::Raw; pub use tail::Tail; pub use within::Within; pub use share::Share; pub use iterwith::IterWith; use crate::FrontMatter; use std::cell::{Ref, RefCell}; use std::rc::Rc; /// Extending the original markdown events iterator /// /// All provided methods in this trait will keep the frontmatter data internally. /// /// Any closure that needs frontmatter as an argument are lazy. With the laziness, the /// frontmatter doesn't have to be presented first, which means it could be updated or /// get replaced during the process. #[must_use = "iterators are lazy and do nothing unless consumed"] pub trait MarkdownExt<T>: Iterator<Item = T> + Sized { /// Different from a normal iterator, `MarkdownExt` must own the frontmatter /// data. fn frontmatter(&mut self) -> &mut Share<RefCell<Option<FrontMatter>>>; /// Inspect the current frontmatter /// /// ## Example /// /// ``` /// # use mercurius::{Bottom, FrontMatter, MarkdownExt}; /// # use std::rc::Rc; /// # use std::cell::RefCell; /// # fn e() -> impl MarkdownExt<u8> { /// # let events: Bottom<u8> = Bottom::new(); /// let mut fm = Rc::new(RefCell::new(None)); /// events.inspect_frontmatter(&mut fm) /// # } /// ``` /// /// The result would be a clone so it's still available even after the whole /// [`MarkdownExt`] chain has been consumed. /// /// [`MarkdownExt`]: trait.MarkdownExt.html fn inspect_frontmatter(mut self, frontmatter: &mut Rc<RefCell<Option<FrontMatter>>>) -> Raw<Self, T> { *frontmatter = self.frontmatter().upgrade().unwrap(); Raw::new(self.frontmatter().transfer().unwrap().into(), self) } /// Place events after the _first_ occurence of a certain event. /// /// ## Example /// /// ``` /// # use mercurius::{Bottom, Event, Tag, FrontMatter, MarkdownExt}; /// # use std::cell::Ref; /// # use std::iter::{empty, Empty}; /// # let events = Bottom::new(); /// # fn days_since_last_update<'e>(_: Ref<Option<FrontMatter>>) -> Empty<Event<'e>> { empty() } /// let outdate = events.after(|e| match &e { /// Event::End(Tag::Heading(1)) => true, /// _ => false, /// }, |frontmatter| days_since_last_update(frontmatter)); /// ``` /// /// Assume the `days_since_last_update` could help create a series of events that notify /// the reader of the number of the days since the last update. The `after` could place the /// new events right after the first heading. fn after<P, F, S, G>(mut self, after: P, f: F) -> After<Self, S, G, F, P, T> where P: Fn(&T) -> bool, S: Iterator<Item = T>, G: IntoIterator<Item = T, IntoIter = S>, F: Fn(Ref<Option<FrontMatter>>) -> G, { After::new( self.frontmatter().transfer().unwrap().into(), self, after, f, ) } /// Place events before the first occurence of a certain event. /// /// ## Example /// /// ``` /// # use mercurius::{Bottom, Event, Tag, FrontMatter, MarkdownExt}; /// # use std::cell::Ref; /// # use std::iter::{empty, Empty}; /// # let events = Bottom::new(); /// # fn disclaimer<'e>(_: Ref<Option<FrontMatter>>) -> Empty<Event<'e>> { empty() } /// let official = events.before(|e| match &e { /// Event::Start(Tag::Heading(1)) => true, /// _ => false, /// }, |frontmatter| disclaimer(frontmatter)); /// ``` /// /// Assume the `disclaimer` could create some events and disclaim something important. /// The `before` here places it before the first heading. fn before<P, F, S, G>(mut self, before: P, f: F) -> Before<Self, S, G, F, P, T> where P: Fn(&T) -> bool, S: Iterator<Item = T>, G: IntoIterator<Item = T, IntoIter = S>, F: Fn(Ref<Option<FrontMatter>>) -> G, { Before::new( self.frontmatter().transfer().unwrap().into(), self, before, f, ) } /// Just like the standard [`chain`] for iterator, the frontmatter will get transferred /// by the following rules. /// /// - If both have frontmatter, the latter one would be picked /// - If neither has frontmatter, it would be `None` /// - Otherwise, the only frontmatter would be used /// /// [`chain`]: https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.chain /// /// ## Example /// /// ``` /// # use mercurius::{Bottom, Event, Parser, MarkdownExt, Result}; /// # use std::cell::Ref; /// # use std::iter::{empty, Empty}; /// # fn main() -> Result<()> { /// # let events: Bottom<Event<'_>> = Bottom::new(); /// # let text = ""; /// let new_events = events.link(Parser::new(text)?); /// # Ok(()) /// # } /// ``` /// /// New events parsed from `text` will be placed after the original events. fn link<E>(mut self, mut other: E) -> Link<Self, E> where E: MarkdownExt<T>, { // Both should be strong let right = other.frontmatter(); let left = self.frontmatter(); // Set both sides to weak and take the only strong one let strong: Share<_> = if right.upgrade().unwrap().borrow().is_some() { *left = right.downgrade().into(); right.transfer().unwrap() } else { *right = left.downgrade().into(); left.transfer().unwrap() }.into(); debug_assert_eq!(strong.count(), (true, true)); debug_assert_eq!(self.frontmatter().count(), (false, true)); debug_assert_eq!(other.frontmatter().count(), (false, true)); Link::new(strong, self, other) } /// Like [`link`], but accept an [`Iterator`]. /// /// For dynamically generating events, consider the [`tail`] method. /// /// [`link`]: trait.MarkdownExt.html#method.link /// [`Iterator`]: https://doc.rust-lang.org/std/iter/trait.Iterator.html /// [`tail`]: trait.MarkdownExt.html#method.tail fn iter<I>(self, iter: I) -> Link<Self, Raw<I, T>> where I: Iterator<Item = T>, { self.link(Raw::new(Rc::new(RefCell::new(None)).into(), iter)) } /// Chain iterator lazily. fn iter_with<I, F>(mut self, f: F) -> IterWith<Self, F, I> where I: Iterator<Item = T>, F: FnOnce() -> I, { IterWith::new(self.frontmatter().transfer().unwrap().into(), self, f) } /// Place events at the start. /// /// ## Example /// /// ``` /// # use mercurius::{Bottom, Event, FrontMatter, MarkdownExt}; /// # use std::cell::Ref; /// # use std::iter::{empty, Empty}; /// # let events = Bottom::new(); /// # fn heading<'e>(_: Ref<Option<FrontMatter>>) -> Empty<Event<'e>> { empty() } /// let with_heading = events.head(|frontmatter| heading(frontmatter)); /// ``` /// /// Assume `heading` reads the `Title` section in the frontmatter and generate a heading /// event, the `head` will place it at the start of the **current** iterator. fn head<F, G, H>(mut self, f: F) -> Head<Self, H, F, G, T> where H: Iterator<Item = T>, G: IntoIterator<Item = T, IntoIter = H>, F: Fn(Ref<Option<FrontMatter>>) -> G, { Head::new(self.frontmatter().transfer().unwrap().into(), self, f) } /// Place events at the end. /// /// ## Example /// /// ``` /// # use mercurius::{Bottom, Event, FrontMatter, MarkdownExt}; /// # use std::cell::Ref; /// # use std::iter::{empty, Empty}; /// # let events = Bottom::new(); /// # fn copyright<'e>(_: Ref<Option<FrontMatter>>) -> Empty<Event<'e>> { empty() } /// let with_copyright = events.tail(|frontmatter| copyright(frontmatter)); /// ``` /// /// Assume `copyright` reads the meta data in the frontmatter and generate copyright related /// event, the `tail` will place it at the end of the **current** iterator. fn tail<F, G, H>(mut self, f: F) -> Tail<Self, H, F, G, T> where H: Iterator<Item = T>, G: IntoIterator<Item = T, IntoIter = H>, F: Fn(Ref<Option<FrontMatter>>) -> G, { Tail::new(self.frontmatter().transfer().unwrap().into(), self, f) } /// Maps function upon events within two specific events. /// /// ## Example /// /// ``` /// # use mercurius::{Bottom, Event, Tag, MarkdownExt}; /// # let events = Bottom::new(); /// let uppercase = events.within(|e| match &e { /// Event::Start(Tag::Heading(_)) => true, /// _ => false, /// }, |e| match &e { /// Event::End(Tag::Heading(_)) => true, /// _ => false, /// }, |_, e| Some(match e { /// Event::Text(s) => Event::Text(s.into_string().to_uppercase().into()), /// event => event, /// })); /// ``` /// /// The above code will turn all text in heading to uppercase. fn within<P, Q, F>(mut self, start: P, end: Q, f: F) -> Within<Self, P, Q, F, T> where P: FnMut(&T) -> bool, Q: FnMut(&T) -> bool, F: FnMut(Ref<Option<FrontMatter>>, T) -> Option<T>, { Within::new( self.frontmatter().transfer().unwrap().into(), self, start, end, f, ) } }