Skip to content

Commit 240fc25

Browse files
committed
Add image/video media support in AsciiDoc-to-Markdown conversion
1 parent 1efcb6a commit 240fc25

File tree

1 file changed

+126
-2
lines changed

1 file changed

+126
-2
lines changed

xtask/src/publish/notes.rs

Lines changed: 126 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ use std::{
55
};
66

77
const LISTING_DELIMITER: &'static str = "----";
8+
const IMAGE_BLOCK_PREFIX: &'static str = "image::";
9+
const VIDEO_BLOCK_PREFIX: &'static str = "video::";
810

911
struct Converter<'a, 'b, R: BufRead> {
1012
iter: &'a mut Peekable<Lines<R>>,
@@ -33,6 +35,12 @@ impl<'a, 'b, R: BufRead> Converter<'a, 'b, R> {
3335
self.process_source_code_block(0)?;
3436
} else if line.starts_with(LISTING_DELIMITER) {
3537
self.process_listing_block(None, 0)?;
38+
} else if line.starts_with('.') {
39+
self.process_block_with_title(0)?;
40+
} else if line.starts_with(IMAGE_BLOCK_PREFIX) {
41+
self.process_image_block(None, 0)?;
42+
} else if line.starts_with(VIDEO_BLOCK_PREFIX) {
43+
self.process_video_block(None, 0)?;
3644
} else {
3745
self.process_paragraph(0)?;
3846
}
@@ -95,6 +103,15 @@ impl<'a, 'b, R: BufRead> Converter<'a, 'b, R> {
95103
} else if line.starts_with(LISTING_DELIMITER) {
96104
self.write_line("", 0);
97105
self.process_listing_block(None, 1)?;
106+
} else if line.starts_with('.') {
107+
self.write_line("", 0);
108+
self.process_block_with_title(1)?;
109+
} else if line.starts_with(IMAGE_BLOCK_PREFIX) {
110+
self.write_line("", 0);
111+
self.process_image_block(None, 1)?;
112+
} else if line.starts_with(VIDEO_BLOCK_PREFIX) {
113+
self.write_line("", 0);
114+
self.process_video_block(None, 1)?;
98115
} else {
99116
self.write_line("", 0);
100117
self.process_paragraph(1)?;
@@ -145,6 +162,75 @@ impl<'a, 'b, R: BufRead> Converter<'a, 'b, R> {
145162
bail!("not a listing block")
146163
}
147164

165+
fn process_block_with_title(&mut self, level: usize) -> anyhow::Result<()> {
166+
if let Some(Ok(line)) = self.iter.next() {
167+
let title =
168+
line.strip_prefix('.').ok_or_else(|| anyhow!("extraction of the title failed"))?;
169+
170+
let line = self
171+
.iter
172+
.peek()
173+
.ok_or_else(|| anyhow!("target block for the title is not found"))?;
174+
let line = line.as_deref().map_err(|e| anyhow!("{e}"))?;
175+
if line.starts_with(IMAGE_BLOCK_PREFIX) {
176+
return self.process_image_block(Some(title), level);
177+
} else if line.starts_with(VIDEO_BLOCK_PREFIX) {
178+
return self.process_video_block(Some(title), level);
179+
} else {
180+
bail!("title for that block type is not supported");
181+
}
182+
}
183+
bail!("not a title")
184+
}
185+
186+
fn process_image_block(&mut self, caption: Option<&str>, level: usize) -> anyhow::Result<()> {
187+
if let Some(Ok(line)) = self.iter.next() {
188+
if let Some((url, attrs)) = parse_media_block(&line, IMAGE_BLOCK_PREFIX) {
189+
let alt = if let Some(stripped) =
190+
attrs.strip_prefix('"').and_then(|s| s.strip_suffix('"'))
191+
{
192+
stripped
193+
} else {
194+
attrs
195+
};
196+
if let Some(caption) = caption {
197+
self.write_caption_line(caption, level);
198+
}
199+
self.write_indent(level);
200+
self.output.push_str("![");
201+
self.output.push_str(alt);
202+
self.output.push_str("](");
203+
self.output.push_str(url);
204+
self.output.push_str(")\n");
205+
return Ok(());
206+
}
207+
}
208+
bail!("not a image block")
209+
}
210+
211+
fn process_video_block(&mut self, caption: Option<&str>, level: usize) -> anyhow::Result<()> {
212+
if let Some(Ok(line)) = self.iter.next() {
213+
if let Some((url, attrs)) = parse_media_block(&line, VIDEO_BLOCK_PREFIX) {
214+
let html_attrs = match attrs {
215+
"options=loop" => "controls loop",
216+
r#"options="autoplay,loop""# => "autoplay controls loop",
217+
_ => bail!("unsupported video syntax"),
218+
};
219+
if let Some(caption) = caption {
220+
self.write_caption_line(caption, level);
221+
}
222+
self.write_indent(level);
223+
self.output.push_str(r#"<video src=""#);
224+
self.output.push_str(url);
225+
self.output.push_str(r#"" "#);
226+
self.output.push_str(html_attrs);
227+
self.output.push_str(">Your browser does not support the video tag.</video>\n");
228+
return Ok(());
229+
}
230+
}
231+
bail!("not a video block")
232+
}
233+
148234
fn process_paragraph(&mut self, level: usize) -> anyhow::Result<()> {
149235
while let Some(line) = self.iter.peek() {
150236
let line = line.as_deref().map_err(|e| anyhow!("{e}"))?;
@@ -192,6 +278,13 @@ impl<'a, 'b, R: BufRead> Converter<'a, 'b, R> {
192278
self.output.push('\n');
193279
}
194280

281+
fn write_caption_line(&mut self, caption: &str, level: usize) {
282+
self.write_indent(level);
283+
self.output.push('_');
284+
self.output.push_str(caption);
285+
self.output.push_str("_\\\n");
286+
}
287+
195288
fn write_indent(&mut self, level: usize) {
196289
for _ in 0..level {
197290
self.output.push_str(" ");
@@ -249,6 +342,17 @@ fn get_list_item(line: &str) -> Option<&str> {
249342
}
250343
}
251344

345+
fn parse_media_block<'a>(line: &'a str, prefix: &str) -> Option<(&'a str, &'a str)> {
346+
if let Some(line) = line.strip_prefix(prefix) {
347+
if let Some((url, rest)) = line.split_once('[') {
348+
if let Some(attrs) = rest.strip_suffix(']') {
349+
return Some((url, attrs));
350+
}
351+
}
352+
}
353+
None
354+
}
355+
252356
#[cfg(test)]
253357
mod tests {
254358
use super::*;
@@ -272,8 +376,18 @@ Release: release:2022-01-01[]
272376
+
273377
image::https://example.com/animation.gif[]
274378
+
379+
image::https://example.com/animation.gif[\"alt text\"]
380+
+
381+
video::https://example.com/movie.mp4[options=loop]
382+
+
275383
video::https://example.com/movie.mp4[options=\"autoplay,loop\"]
276384
+
385+
.Image
386+
image::https://example.com/animation.gif[]
387+
+
388+
.Video
389+
video::https://example.com/movie.mp4[options=loop]
390+
+
277391
[source,bash]
278392
----
279393
rustup update nightly
@@ -325,9 +439,19 @@ Release: release:2022-01-01[]
325439
- pr:1111[] foo bar baz
326440
- pr:2222[] foo bar baz
327441
328-
image::https://example.com/animation.gif[]
442+
![](https://example.com/animation.gif)
443+
444+
![alt text](https://example.com/animation.gif)
445+
446+
<video src=\"https://example.com/movie.mp4\" controls loop>Your browser does not support the video tag.</video>
447+
448+
<video src=\"https://example.com/movie.mp4\" autoplay controls loop>Your browser does not support the video tag.</video>
449+
450+
_Image_\\
451+
![](https://example.com/animation.gif)
329452
330-
video::https://example.com/movie.mp4[options=\"autoplay,loop\"]
453+
_Video_\\
454+
<video src=\"https://example.com/movie.mp4\" controls loop>Your browser does not support the video tag.</video>
331455
332456
```bash
333457
rustup update nightly

0 commit comments

Comments
 (0)