Skip to content

Commit 451fe12

Browse files
authored
Merge pull request #601 from kivikakk/push-lsmztupslltn
Replace io::Write with fmt::Write to avoid revalidating UTF-8.
2 parents de6f94b + 0f030ac commit 451fe12

32 files changed

+607
-610
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,4 @@ benches/comrak-*
99
benches/pulldown-cmark
1010
benches/markdown-it
1111
/result
12+
/bench-output.md

Cargo.lock

Lines changed: 7 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ emojis = { version = "0.6.2", optional = true }
4545
arbitrary = { version = "1", optional = true, features = ["derive"] }
4646
bon = { version = "3", optional = true }
4747
caseless = "0.2.1"
48+
fmt2io = { version = "1.0.0", optional = true }
4849

4950
[dev-dependencies]
5051
ntest = "0.9"
@@ -54,7 +55,7 @@ toml = "0.7.3"
5455

5556
[features]
5657
default = ["cli", "syntect", "bon"]
57-
cli = ["clap", "shell-words", "xdg"]
58+
cli = ["clap", "shell-words", "xdg", "fmt2io"]
5859
shortcodes = ["emojis"]
5960
bon = ["dep:bon"]
6061

Makefile

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,13 @@ bench:
1010
cargo build --release
1111
(cd vendor/cmark-gfm/; make bench PROG=../../target/release/comrak)
1212

13-
binaries: build-comrak-branch build-comrak-master build-cmark-gfm build-pulldown-cmark build-markdown-it
13+
binaries: build-comrak-branch build-comrak-main build-cmark-gfm build-pulldown-cmark build-markdown-it
1414

1515
build-comrak-branch:
1616
cargo build --release
1717
cp ${ROOT}/target/release/comrak ${ROOT}/benches/comrak-${COMMIT}
1818

19-
build-comrak-master:
19+
build-comrak-main:
2020
git clone https://github.com/kivikakk/comrak.git --depth 1 --single-branch ${ROOT}/vendor/comrak || true
2121
cd ${ROOT}/vendor/comrak && \
2222
cargo build --release && \
@@ -40,11 +40,17 @@ build-pulldown-cmark:
4040
bench-comrak: build-comrak-branch
4141
git clone https://github.com/progit/progit.git ${ROOT}/vendor/progit || true > /dev/null
4242
cd benches && \
43-
hyperfine --warmup 3 --min-runs ${MIN_RUNS} -L binary comrak-${COMMIT} './bench.sh ./{binary}'
43+
hyperfine --warmup 3 --min-runs ${MIN_RUNS} -L binary comrak-${COMMIT} './bench.sh ./{binary}'
44+
45+
bench-comrak-vs-main: build-comrak-branch build-comrak-main
46+
git clone https://github.com/progit/progit.git ${ROOT}/vendor/progit || true > /dev/null
47+
cd benches && \
48+
hyperfine --warmup 10 --min-runs ${MIN_RUNS} -L binary comrak-${COMMIT},comrak-main './bench.sh ./{binary}' --export-markdown ${ROOT}/bench-output.md &&\
49+
echo "\n\nRun on" `date -u` >> ${ROOT}/bench-output.md
4450

4551
bench-all: binaries
4652
git clone https://github.com/progit/progit.git ${ROOT}/vendor/progit || true > /dev/null
4753
cd benches && \
48-
hyperfine --warmup 10 --min-runs ${MIN_RUNS} -L binary comrak-${COMMIT},comrak-main,pulldown-cmark,cmark-gfm,markdown-it './bench.sh ./{binary}' --export-markdown ${ROOT}/bench-output.md &&\
54+
hyperfine --warmup 10 --min-runs ${MIN_RUNS} -L binary comrak-${COMMIT},comrak-main,pulldown-cmark,cmark-gfm,markdown-it './bench.sh ./{binary}' --export-markdown ${ROOT}/bench-output.md &&\
4955
echo "\n\nRun on" `date -u` >> ${ROOT}/bench-output.md
5056

benches/progit.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ fn bench_progit(b: &mut Bencher) {
1616
b.iter(|| {
1717
let arena = Arena::new();
1818
let root = parse_document(&arena, &s, &Options::default());
19-
let mut output = vec![];
19+
let mut output = String::new();
2020
format_html(root, &Options::default(), &mut output).unwrap()
2121
});
2222
}

changelog.txt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,10 @@
1+
# [v0.43.0] - unreleased
2+
3+
* `html::format_document`, `xml::format_document`, `cm::format_document` and
4+
friends now take an `std::fmt::Write` as their `output` argument, instead of
5+
an `std::io::Write`.
6+
7+
18
# [v0.42.0] - 2025-09-24
29

310
New APIs:

examples/custom_formatter.rs

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,22 @@
11
use comrak::html::ChildRendering;
22
use comrak::{create_formatter, nodes::NodeValue};
3-
use std::io::Write;
3+
use std::fmt::Write;
44

55
create_formatter!(CustomFormatter, {
66
NodeValue::Emph => |context, entering| {
77
if entering {
8-
context.write_all(b"<i>")?;
8+
context.write_str("<i>")?;
99
} else {
10-
context.write_all(b"</i>")?;
10+
context.write_str("</i>")?;
1111
}
1212
},
1313
NodeValue::Strong => |context, entering| {
14-
context.write_all(if entering { b"<b>" } else { b"</b>" })?;
14+
context.write_str(if entering { "<b>" } else { "</b>" })?;
1515
},
1616
NodeValue::Image(ref nl) => |context, node, entering| {
1717
assert!(node.data.borrow().sourcepos == (3, 1, 3, 18).into());
1818
if entering {
19-
context.write_all(nl.url.to_uppercase().as_bytes())?;
19+
context.write_str(&nl.url.to_uppercase())?;
2020
}
2121
return Ok(ChildRendering::Skip);
2222
},
@@ -33,11 +33,8 @@ fn main() {
3333
&options,
3434
);
3535

36-
let mut buf: Vec<u8> = vec![];
37-
CustomFormatter::format_document(doc, &options, &mut buf).unwrap();
36+
let mut out = String::new();
37+
CustomFormatter::format_document(doc, &options, &mut out).unwrap();
3838

39-
assert_eq!(
40-
std::str::from_utf8(&buf).unwrap(),
41-
"<p><i>Hello</i>, <b>world</b>.</p>\n<p>/IMG.PNG</p>\n"
42-
);
39+
assert_eq!(out, "<p><i>Hello</i>, <b>world</b>.</p>\n<p>/IMG.PNG</p>\n");
4340
}

examples/custom_formatter_alt_text.rs

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ fn formatter<'a>(
3434
context: &mut comrak::html::Context,
3535
node: &'a comrak::nodes::AstNode<'a>,
3636
entering: bool,
37-
) -> std::io::Result<comrak::html::ChildRendering> {
37+
) -> Result<comrak::html::ChildRendering, std::fmt::Error> {
3838
let mut borrow = node.data.borrow_mut();
3939
if let NodeValue::Image(ref mut nl) = borrow.value {
4040
autotitle_images(nl, context, node, entering);
@@ -47,7 +47,7 @@ fn main() {
4747
let arena = Arena::new();
4848
let parsed = parse_document(&arena, "![my epic image](/img.png)", &Default::default());
4949

50-
let mut out = Vec::new();
50+
let mut out = String::new();
5151
comrak::html::format_document_with_formatter(
5252
parsed,
5353
&Default::default(),
@@ -58,6 +58,5 @@ fn main() {
5858
)
5959
.unwrap_or_else(|_| unreachable!("writing to Vec<u8> cannot fail"));
6060

61-
// SAFETY: the formatter always emits valid UTF-8
62-
println!("{}", unsafe { String::from_utf8_unchecked(out) });
61+
println!("{out}");
6362
}

examples/custom_formatter_user.rs

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,24 @@
11
use comrak::html::ChildRendering;
22
use comrak::{create_formatter, nodes::NodeValue};
3-
use std::io::Write;
3+
use std::fmt::Write;
44

55
create_formatter!(CustomFormatter<usize>, {
66
NodeValue::Emph => |context, entering| {
77
context.user += 1;
88
if entering {
9-
context.write_all(b"<i>")?;
9+
context.write_str("<i>")?;
1010
} else {
11-
context.write_all(b"</i>")?;
11+
context.write_str("</i>")?;
1212
}
1313
},
1414
NodeValue::Strong => |context, entering| {
1515
context.user += 1;
16-
context.write_all(if entering { b"<b>" } else { b"</b>" })?;
16+
context.write_str(if entering { "<b>" } else { "</b>" })?;
1717
},
1818
NodeValue::Image(ref nl) => |context, node, entering| {
1919
assert!(node.data.borrow().sourcepos == (3, 1, 3, 18).into());
2020
if entering {
21-
context.write_all(nl.url.to_uppercase().as_bytes())?;
21+
context.write_str(&nl.url.to_uppercase())?;
2222
}
2323
return Ok(ChildRendering::Skip);
2424
},
@@ -35,13 +35,10 @@ fn main() {
3535
&options,
3636
);
3737

38-
let mut buf: Vec<u8> = vec![];
39-
let converted_count = CustomFormatter::format_document(doc, &options, &mut buf, 0).unwrap();
38+
let mut out = String::new();
39+
let converted_count = CustomFormatter::format_document(doc, &options, &mut out, 0).unwrap();
4040

41-
assert_eq!(
42-
std::str::from_utf8(&buf).unwrap(),
43-
"<p><i>Hello</i>, <b>world</b>.</p>\n<p>/IMG.PNG</p>\n"
44-
);
41+
assert_eq!(out, "<p><i>Hello</i>, <b>world</b>.</p>\n<p>/IMG.PNG</p>\n");
4542

4643
assert_eq!(converted_count, 4);
4744
}

examples/custom_headings.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use comrak::{
44
nodes::Sourcepos,
55
Options, Plugins,
66
};
7-
use std::io::{self, Write};
7+
use std::fmt::{self, Write};
88

99
fn main() {
1010
let adapter = CustomHeadingAdapter;
@@ -39,7 +39,7 @@ impl HeadingAdapter for CustomHeadingAdapter {
3939
output: &mut dyn Write,
4040
heading: &HeadingMeta,
4141
sourcepos: Option<Sourcepos>,
42-
) -> io::Result<()> {
42+
) -> fmt::Result {
4343
let id = slug::slugify(&heading.content);
4444

4545
let search_include = !&heading.content.contains("hide");
@@ -57,7 +57,7 @@ impl HeadingAdapter for CustomHeadingAdapter {
5757
)
5858
}
5959

60-
fn exit(&self, output: &mut dyn Write, heading: &HeadingMeta) -> io::Result<()> {
60+
fn exit(&self, output: &mut dyn Write, heading: &HeadingMeta) -> fmt::Result {
6161
write!(output, "</h{}>", heading.level)
6262
}
6363
}

0 commit comments

Comments
 (0)