about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--xtask/src/publish/notes.rs128
1 files changed, 126 insertions, 2 deletions
diff --git a/xtask/src/publish/notes.rs b/xtask/src/publish/notes.rs
index 257c30a208b..c6b743ee038 100644
--- a/xtask/src/publish/notes.rs
+++ b/xtask/src/publish/notes.rs
@@ -5,6 +5,8 @@ use std::{
 };
 
 const LISTING_DELIMITER: &'static str = "----";
+const IMAGE_BLOCK_PREFIX: &'static str = "image::";
+const VIDEO_BLOCK_PREFIX: &'static str = "video::";
 
 struct Converter<'a, 'b, R: BufRead> {
     iter: &'a mut Peekable<Lines<R>>,
@@ -33,6 +35,12 @@ impl<'a, 'b, R: BufRead> Converter<'a, 'b, R> {
                 self.process_source_code_block(0)?;
             } else if line.starts_with(LISTING_DELIMITER) {
                 self.process_listing_block(None, 0)?;
+            } else if line.starts_with('.') {
+                self.process_block_with_title(0)?;
+            } else if line.starts_with(IMAGE_BLOCK_PREFIX) {
+                self.process_image_block(None, 0)?;
+            } else if line.starts_with(VIDEO_BLOCK_PREFIX) {
+                self.process_video_block(None, 0)?;
             } else {
                 self.process_paragraph(0)?;
             }
@@ -95,6 +103,15 @@ impl<'a, 'b, R: BufRead> Converter<'a, 'b, R> {
                 } else if line.starts_with(LISTING_DELIMITER) {
                     self.write_line("", 0);
                     self.process_listing_block(None, 1)?;
+                } else if line.starts_with('.') {
+                    self.write_line("", 0);
+                    self.process_block_with_title(1)?;
+                } else if line.starts_with(IMAGE_BLOCK_PREFIX) {
+                    self.write_line("", 0);
+                    self.process_image_block(None, 1)?;
+                } else if line.starts_with(VIDEO_BLOCK_PREFIX) {
+                    self.write_line("", 0);
+                    self.process_video_block(None, 1)?;
                 } else {
                     self.write_line("", 0);
                     self.process_paragraph(1)?;
@@ -145,6 +162,75 @@ impl<'a, 'b, R: BufRead> Converter<'a, 'b, R> {
         bail!("not a listing block")
     }
 
+    fn process_block_with_title(&mut self, level: usize) -> anyhow::Result<()> {
+        if let Some(Ok(line)) = self.iter.next() {
+            let title =
+                line.strip_prefix('.').ok_or_else(|| anyhow!("extraction of the title failed"))?;
+
+            let line = self
+                .iter
+                .peek()
+                .ok_or_else(|| anyhow!("target block for the title is not found"))?;
+            let line = line.as_deref().map_err(|e| anyhow!("{e}"))?;
+            if line.starts_with(IMAGE_BLOCK_PREFIX) {
+                return self.process_image_block(Some(title), level);
+            } else if line.starts_with(VIDEO_BLOCK_PREFIX) {
+                return self.process_video_block(Some(title), level);
+            } else {
+                bail!("title for that block type is not supported");
+            }
+        }
+        bail!("not a title")
+    }
+
+    fn process_image_block(&mut self, caption: Option<&str>, level: usize) -> anyhow::Result<()> {
+        if let Some(Ok(line)) = self.iter.next() {
+            if let Some((url, attrs)) = parse_media_block(&line, IMAGE_BLOCK_PREFIX) {
+                let alt = if let Some(stripped) =
+                    attrs.strip_prefix('"').and_then(|s| s.strip_suffix('"'))
+                {
+                    stripped
+                } else {
+                    attrs
+                };
+                if let Some(caption) = caption {
+                    self.write_caption_line(caption, level);
+                }
+                self.write_indent(level);
+                self.output.push_str("![");
+                self.output.push_str(alt);
+                self.output.push_str("](");
+                self.output.push_str(url);
+                self.output.push_str(")\n");
+                return Ok(());
+            }
+        }
+        bail!("not a image block")
+    }
+
+    fn process_video_block(&mut self, caption: Option<&str>, level: usize) -> anyhow::Result<()> {
+        if let Some(Ok(line)) = self.iter.next() {
+            if let Some((url, attrs)) = parse_media_block(&line, VIDEO_BLOCK_PREFIX) {
+                let html_attrs = match attrs {
+                    "options=loop" => "controls loop",
+                    r#"options="autoplay,loop""# => "autoplay controls loop",
+                    _ => bail!("unsupported video syntax"),
+                };
+                if let Some(caption) = caption {
+                    self.write_caption_line(caption, level);
+                }
+                self.write_indent(level);
+                self.output.push_str(r#"<video src=""#);
+                self.output.push_str(url);
+                self.output.push_str(r#"" "#);
+                self.output.push_str(html_attrs);
+                self.output.push_str(">Your browser does not support the video tag.</video>\n");
+                return Ok(());
+            }
+        }
+        bail!("not a video block")
+    }
+
     fn process_paragraph(&mut self, level: usize) -> anyhow::Result<()> {
         while let Some(line) = self.iter.peek() {
             let line = line.as_deref().map_err(|e| anyhow!("{e}"))?;
@@ -192,6 +278,13 @@ impl<'a, 'b, R: BufRead> Converter<'a, 'b, R> {
         self.output.push('\n');
     }
 
+    fn write_caption_line(&mut self, caption: &str, level: usize) {
+        self.write_indent(level);
+        self.output.push('_');
+        self.output.push_str(caption);
+        self.output.push_str("_\\\n");
+    }
+
     fn write_indent(&mut self, level: usize) {
         for _ in 0..level {
             self.output.push_str("  ");
@@ -249,6 +342,17 @@ fn get_list_item(line: &str) -> Option<&str> {
     }
 }
 
+fn parse_media_block<'a>(line: &'a str, prefix: &str) -> Option<(&'a str, &'a str)> {
+    if let Some(line) = line.strip_prefix(prefix) {
+        if let Some((url, rest)) = line.split_once('[') {
+            if let Some(attrs) = rest.strip_suffix(']') {
+                return Some((url, attrs));
+            }
+        }
+    }
+    None
+}
+
 #[cfg(test)]
 mod tests {
     use super::*;
@@ -272,8 +376,18 @@ Release: release:2022-01-01[]
 +
 image::https://example.com/animation.gif[]
 +
+image::https://example.com/animation.gif[\"alt text\"]
++
+video::https://example.com/movie.mp4[options=loop]
++
 video::https://example.com/movie.mp4[options=\"autoplay,loop\"]
 +
+.Image
+image::https://example.com/animation.gif[]
++
+.Video
+video::https://example.com/movie.mp4[options=loop]
++
 [source,bash]
 ----
 rustup update nightly
@@ -325,9 +439,19 @@ Release: release:2022-01-01[]
 - pr:1111[] foo bar baz
 - pr:2222[] foo bar baz
 
-  image::https://example.com/animation.gif[]
+  ![](https://example.com/animation.gif)
+
+  ![alt text](https://example.com/animation.gif)
+
+  <video src=\"https://example.com/movie.mp4\" controls loop>Your browser does not support the video tag.</video>
+
+  <video src=\"https://example.com/movie.mp4\" autoplay controls loop>Your browser does not support the video tag.</video>
+
+  _Image_\\
+  ![](https://example.com/animation.gif)
 
-  video::https://example.com/movie.mp4[options=\"autoplay,loop\"]
+  _Video_\\
+  <video src=\"https://example.com/movie.mp4\" controls loop>Your browser does not support the video tag.</video>
 
   ```bash
   rustup update nightly