Skip to content

Commit

Permalink
feat!: Simplify cmark(…) by removing State parameter, introduce `…
Browse files Browse the repository at this point in the history
…cmark_resume(…)`.

The reason for doing it this way around causing a breaking change is
the change of behaviour for `cmark_resume`, which now returns a state
instance that needs a `finalize()` call to flush certain caches.

Currently this is only used to write link blocks, after which the state
is still usable for future invocations if necessary.

That way the caller has control over where to put link blocks, or other
items that should be placed at the end of a logical section.
  • Loading branch information
Byron committed Jan 28, 2022
1 parent 6a42312 commit 7166abe
Show file tree
Hide file tree
Showing 6 changed files with 82 additions and 70 deletions.
9 changes: 6 additions & 3 deletions examples/stupicat.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use std::{
};

use pulldown_cmark::{Options, Parser};
use pulldown_cmark_to_cmark::cmark;
use pulldown_cmark_to_cmark::{cmark, cmark_resume};

fn main() -> Result<(), Box<dyn std::error::Error>> {
let path = env::args_os()
Expand All @@ -23,10 +23,13 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
if event_by_event {
let mut state = None;
for event in Parser::new_ext(&md, options) {
state = Some(cmark(std::iter::once(event), &mut buf, state.take())?);
state = cmark_resume(std::iter::once(event), &mut buf, state.take())?.into();
}
if let Some(state) = state {
state.finalize(&mut buf)?;
}
} else {
cmark(Parser::new_ext(&md, options), &mut buf, None)?;
cmark(Parser::new_ext(&md, options), &mut buf)?;
}

stdout().write_all(buf.as_bytes())?;
Expand Down
114 changes: 59 additions & 55 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ impl<'a> Options<'a> {
/// *Returns* the [`State`] of the serialization on success. You can use it as initial state in the
/// next call if you are halting event serialization.
/// *Errors* are only happening if the underlying buffer fails, which is unlikely.
pub fn cmark_with_options<'a, I, E, F>(
pub fn cmark_resume_with_options<'a, I, E, F>(
events: I,
mut formatter: F,
state: Option<State<'static>>,
Expand All @@ -149,11 +149,7 @@ where
E: Borrow<Event<'a>>,
F: fmt::Write,
{
let mut event_by_event = true;
let mut state = state.unwrap_or_else(|| {
event_by_event = false;
State::default()
});
let mut state = state.unwrap_or_default();
fn padding<'a, F>(f: &mut F, p: &[Cow<'a, str>]) -> fmt::Result
where
F: fmt::Write,
Expand Down Expand Up @@ -222,37 +218,6 @@ where
}
}

fn close_link<F>(uri: &str, title: &str, f: &mut F, link_type: LinkType, event_by_event: bool) -> fmt::Result
where
F: fmt::Write,
{
let close = format!(
"{}{}",
match event_by_event {
true if matches!(link_type, LinkType::Shortcut) => "",
_ => "]",
},
match event_by_event {
false if matches!(link_type, LinkType::Shortcut) => ": ",
_ => "(",
}
);

if uri.contains(' ') {
write!(f, "{}<{uri}>", close, uri = uri)?;
} else {
write!(f, "{}{uri}", close, uri = uri)?;
}
if !title.is_empty() {
write!(f, " \"{title}\"", title = title)?;
}
if link_type != LinkType::Shortcut || event_by_event {
f.write_char(')')?;
}

Ok(())
}

for event in events {
use pulldown_cmark::{CodeBlockKind, Event::*, Tag::*};

Expand Down Expand Up @@ -405,7 +370,7 @@ where
formatter.write_char(']')
}
Image(_, ref uri, ref title) | Link(_, ref uri, ref title) => {
close_link(uri, title, &mut formatter, LinkType::Inline, event_by_event)
close_link(uri, title, &mut formatter, LinkType::Inline)
}
Emphasis => formatter.write_char(options.emphasis_token),
Strong => formatter.write_str(options.strong_token),
Expand Down Expand Up @@ -556,33 +521,72 @@ where
}
}?
}
if !state.shortcuts.is_empty() {
if !event_by_event {
formatter.write_str("\n")?;
Ok(state)
}

fn close_link<F>(uri: &str, title: &str, f: &mut F, link_type: LinkType) -> fmt::Result
where
F: fmt::Write,
{
let separator = match link_type {
LinkType::Shortcut => ": ",
_ => "(",
};

if uri.contains(' ') {
write!(f, "]{}<{uri}>", separator, uri = uri)?;
} else {
write!(f, "]{}{uri}", separator, uri = uri)?;
}
if !title.is_empty() {
write!(f, " \"{title}\"", title = title)?;
}
if link_type != LinkType::Shortcut {
f.write_char(')')?;
}

Ok(())
}

impl<'a> State<'a> {
pub fn finalize<F>(mut self, mut formatter: F) -> Result<Self, fmt::Error>
where
F: fmt::Write,
{
if self.shortcuts.is_empty() {
return Ok(self);
}
for shortcut in state.shortcuts.drain(..) {
if !event_by_event {
write!(formatter, "\n[{}", shortcut.0)?;
}

close_link(
&shortcut.1,
&shortcut.2,
&mut formatter,
LinkType::Shortcut,
event_by_event,
)?
formatter.write_str("\n")?;
for shortcut in self.shortcuts.drain(..) {
write!(formatter, "\n[{}", shortcut.0)?;
close_link(&shortcut.1, &shortcut.2, &mut formatter, LinkType::Shortcut)?
}
Ok(self)
}
Ok(state)
}

/// As [`cmark_with_options()`], but with default [`Options`].
pub fn cmark<'a, I, E, F>(events: I, formatter: F, state: Option<State<'static>>) -> Result<State<'static>, fmt::Error>
pub fn cmark<'a, I, E, F>(events: I, mut formatter: F) -> Result<State<'static>, fmt::Error>
where
I: Iterator<Item = E>,
E: Borrow<Event<'a>>,
F: fmt::Write,
{
let state = cmark_resume_with_options(events, &mut formatter, Default::default(), Options::default())?;
state.finalize(formatter)
}

/// As [`cmark_resume_with_options()`], but with default [`Options`].
pub fn cmark_resume<'a, I, E, F>(
events: I,
formatter: F,
state: Option<State<'static>>,
) -> Result<State<'static>, fmt::Error>
where
I: Iterator<Item = E>,
E: Borrow<Event<'a>>,
F: fmt::Write,
{
cmark_with_options(events, formatter, state, Options::default())
cmark_resume_with_options(events, formatter, state, Options::default())
}
2 changes: 1 addition & 1 deletion tests/display.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use pulldown_cmark_to_cmark::*;

fn s(e: Event) -> String {
let mut buf = String::new();
cmark([e].iter(), &mut buf, None).unwrap();
cmark([e].iter(), &mut buf).unwrap();
buf
}
mod code {
Expand Down
13 changes: 9 additions & 4 deletions tests/fixtures/snapshots/stupicat-event-by-event-output
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,11 @@ voluptua. `At vero eos et` accusam et

###### Level 6

## [Links](http://www.example.com/shortcut)
## [Links]

Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod
tempor invidunt ut labore et dolore magna aliquyam erat
(<http://www.example.com/autolink>), sed [`diam`](http://www.example.com/shortcut_code_diam) [`voluptua`](http://www.example.com/shortcut_code_voluptua).
(<http://www.example.com/autolink>), sed [`diam`] [`voluptua`].

Lorem ipsum dolor sit amet, [consetetur
sadipscing](http://www.example.com/inline) elitr, sed diam nonumy eirmod tempor
Expand All @@ -30,7 +30,7 @@ sea [takimata sanctus](./showcase.md) est Lorem ipsum dolor sit amet.

Ask to <user@example.com>.

## [Images](http://www.example.com/another_shortcut)
## [Images]

Images as blocks:

Expand Down Expand Up @@ -201,4 +201,9 @@ foo
```

* | < > #
````
````

[Links]: http://www.example.com/shortcut
[`diam`]: http://www.example.com/shortcut_code_diam
[`voluptua`]: http://www.example.com/shortcut_code_voluptua
[Images]: http://www.example.com/another_shortcut
12 changes: 6 additions & 6 deletions tests/fmt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,29 +2,29 @@
extern crate indoc;

use pulldown_cmark::{Alignment, CodeBlockKind, Event, LinkType, Options, Parser, Tag};
use pulldown_cmark_to_cmark::{cmark, cmark_with_options, Options as CmarkToCmarkOptions, State};
use pulldown_cmark_to_cmark::{cmark, cmark_resume, cmark_resume_with_options, Options as CmarkToCmarkOptions, State};

fn fmts(s: &str) -> (String, State<'static>) {
let mut buf = String::new();
let s = cmark(Parser::new_ext(s, Options::all()), &mut buf, None).unwrap();
let s = cmark(Parser::new_ext(s, Options::all()), &mut buf).unwrap();
(buf, s)
}

fn fmts_with_options(s: &str, options: CmarkToCmarkOptions) -> (String, State<'static>) {
let mut buf = String::new();
let s = cmark_with_options(Parser::new_ext(s, Options::all()), &mut buf, None, options).unwrap();
let s = cmark_resume_with_options(Parser::new_ext(s, Options::all()), &mut buf, None, options).unwrap();
(buf, s)
}

fn fmtes(e: &[Event], s: State<'static>) -> (String, State<'static>) {
let mut buf = String::new();
let s = cmark(e.iter(), &mut buf, Some(s)).unwrap();
let s = cmark_resume(e.iter(), &mut buf, Some(s)).unwrap();
(buf, s)
}

fn fmte<'a>(e: impl AsRef<[Event<'a>]>) -> (String, State<'static>) {
let mut buf = String::new();
let s = cmark(e.as_ref().iter(), &mut buf, None).unwrap();
let s = cmark(e.as_ref().iter(), &mut buf).unwrap();
(buf, s)
}

Expand All @@ -34,7 +34,7 @@ fn assert_events_eq(s: &str) {
let before_events = Parser::new_ext(s, Options::all());

let mut buf = String::new();
cmark(before_events, &mut buf, None).unwrap();
cmark(before_events, &mut buf).unwrap();

let before_events = Parser::new_ext(s, Options::all());
let after_events = Parser::new_ext(&buf, Options::all());
Expand Down
2 changes: 1 addition & 1 deletion tests/spec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ fn test_roundtrip(original: &str, expected: &str) -> bool {
let opts = Options::empty();
let event_list = Parser::new_ext(original, opts).collect::<Vec<_>>();
let mut regen_str = String::new();
cmark(event_list.iter().cloned(), &mut regen_str, None).expect("Regeneration failure");
cmark(event_list.iter().cloned(), &mut regen_str).expect("Regeneration failure");
let event_list_2 = Parser::new_ext(&regen_str, opts).collect::<Vec<_>>();
let event_count = event_list.len();
let event_count_2 = event_list_2.len();
Expand Down

0 comments on commit 7166abe

Please sign in to comment.