Эх сурвалжийг харах

changelogs: add support for parsing rustc changelogs

Pietro Albini 4 жил өмнө
parent
commit
5299997572
5 өөрчлөгдсөн 289 нэмэгдсэн , 0 устгасан
  1. 111 0
      Cargo.lock
  2. 1 0
      Cargo.toml
  3. 26 0
      src/changelogs/mod.rs
  4. 150 0
      src/changelogs/rustc.rs
  5. 1 0
      src/lib.rs

+ 111 - 0
Cargo.lock

@@ -24,6 +24,15 @@ dependencies = [
  "memchr",
 ]
 
+[[package]]
+name = "ansi_term"
+version = "0.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b"
+dependencies = [
+ "winapi 0.3.9",
+]
+
 [[package]]
 name = "anyhow"
 version = "1.0.32"
@@ -177,6 +186,21 @@ dependencies = [
  "time",
 ]
 
+[[package]]
+name = "clap"
+version = "2.33.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "37e58ac78573c40708d45522f0d80fa2f01cc4f9b4e2bf749807255454312002"
+dependencies = [
+ "ansi_term",
+ "atty",
+ "bitflags",
+ "strsim",
+ "textwrap",
+ "unicode-width",
+ "vec_map",
+]
+
 [[package]]
 name = "cloudabi"
 version = "0.1.0"
@@ -186,6 +210,25 @@ dependencies = [
  "bitflags",
 ]
 
+[[package]]
+name = "comrak"
+version = "0.8.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0d325e4f2ffff52ca77d995bb675494d5364aa332499d5f7c7fbb28c25e671f6"
+dependencies = [
+ "clap",
+ "entities",
+ "lazy_static",
+ "pest",
+ "pest_derive",
+ "regex",
+ "shell-words",
+ "twoway",
+ "typed-arena",
+ "unicode_categories",
+ "xdg",
+]
+
 [[package]]
 name = "core-foundation"
 version = "0.7.0"
@@ -274,6 +317,12 @@ dependencies = [
  "cfg-if",
 ]
 
+[[package]]
+name = "entities"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b5320ae4c3782150d900b79807611a59a99fc9a1d61d686faafc24b93fc8d7ca"
+
 [[package]]
 name = "env_logger"
 version = "0.7.1"
@@ -1489,6 +1538,12 @@ dependencies = [
  "opaque-debug 0.3.0",
 ]
 
+[[package]]
+name = "shell-words"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b6fa3938c99da4914afedd13bf3d79bcb6c277d1b2c398d23257a304d9e1b074"
+
 [[package]]
 name = "siphasher"
 version = "0.3.3"
@@ -1551,6 +1606,12 @@ dependencies = [
  "unicode-normalization",
 ]
 
+[[package]]
+name = "strsim"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
+
 [[package]]
 name = "subtle"
 version = "2.2.3"
@@ -1606,6 +1667,15 @@ dependencies = [
  "winapi-util",
 ]
 
+[[package]]
+name = "textwrap"
+version = "0.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060"
+dependencies = [
+ "unicode-width",
+]
+
 [[package]]
 name = "thread_local"
 version = "1.0.1"
@@ -1752,6 +1822,7 @@ dependencies = [
  "anyhow",
  "async-trait",
  "chrono",
+ "comrak",
  "dotenv",
  "env_logger",
  "futures",
@@ -1786,6 +1857,22 @@ version = "0.2.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642"
 
+[[package]]
+name = "twoway"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6b40075910de3a912adbd80b5d8bad6ad10a23eeb1f5bf9d4006839e899ba5bc"
+dependencies = [
+ "memchr",
+ "unchecked-index",
+]
+
+[[package]]
+name = "typed-arena"
+version = "1.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a9b2228007eba4120145f785df0f6c92ea538f5a3635a612ecf4e334c8c1446d"
+
 [[package]]
 name = "typenum"
 version = "1.12.0"
@@ -1798,6 +1885,12 @@ version = "0.1.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "56dee185309b50d1f11bfedef0fe6d036842e3fb77413abef29f8f8d1c5d4c1c"
 
+[[package]]
+name = "unchecked-index"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "eeba86d422ce181a719445e51872fa30f1f7413b62becb52e95ec91aa262d85c"
+
 [[package]]
 name = "unicase"
 version = "2.6.0"
@@ -1837,6 +1930,12 @@ version = "0.2.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564"
 
+[[package]]
+name = "unicode_categories"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "39ec24b3121d976906ece63c9daad25b85969647682eee313cb5779fdd69e14e"
+
 [[package]]
 name = "url"
 version = "2.1.1"
@@ -1864,6 +1963,12 @@ version = "0.2.10"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "6454029bf181f092ad1b853286f23e2c507d8e8194d01d92da4a55c274a5508c"
 
+[[package]]
+name = "vec_map"
+version = "0.8.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191"
+
 [[package]]
 name = "version_check"
 version = "0.9.2"
@@ -2042,3 +2147,9 @@ dependencies = [
  "winapi 0.2.8",
  "winapi-build",
 ]
+
+[[package]]
+name = "xdg"
+version = "2.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d089681aa106a86fade1b0128fb5daf07d5867a509ab036d99988dec80429a57"

+ 1 - 0
Cargo.toml

@@ -34,6 +34,7 @@ postgres-native-tls = "0.3"
 native-tls = "0.2"
 serde_path_to_error = "0.1.2"
 octocrab = "0.5"
+comrak = "0.8.2"
 
 [dependencies.serde]
 version = "1"

+ 26 - 0
src/changelogs/mod.rs

@@ -0,0 +1,26 @@
+mod rustc;
+
+use comrak::Arena;
+use std::collections::HashMap;
+
+#[derive(Copy, Clone, PartialEq, Eq, Debug, serde::Deserialize)]
+#[serde(rename_all = "kebab-case")]
+pub(crate) enum ChangelogFormat {
+    Rustc,
+}
+
+pub(crate) struct Changelog {
+    versions: HashMap<String, String>,
+}
+
+impl Changelog {
+    pub(crate) fn parse(format: ChangelogFormat, content: &str) -> anyhow::Result<Self> {
+        match format {
+            ChangelogFormat::Rustc => rustc::RustcFormat::new(&Arena::new()).parse(content),
+        }
+    }
+
+    pub(crate) fn version(&self, version: &str) -> Option<&str> {
+        self.versions.get(version).map(|s| s.as_str())
+    }
+}

+ 150 - 0
src/changelogs/rustc.rs

@@ -0,0 +1,150 @@
+use super::Changelog;
+use comrak::{
+    nodes::{Ast, AstNode, NodeHeading, NodeValue},
+    Arena, ComrakOptions,
+};
+use std::cell::RefCell;
+use std::collections::HashMap;
+
+pub(super) struct RustcFormat<'a> {
+    arena: &'a Arena<AstNode<'a>>,
+    current_h1: Option<String>,
+    result: Changelog,
+}
+
+impl<'a> RustcFormat<'a> {
+    pub(super) fn new(arena: &'a Arena<AstNode<'a>>) -> Self {
+        RustcFormat {
+            arena,
+            current_h1: None,
+            result: Changelog {
+                versions: HashMap::new(),
+            },
+        }
+    }
+
+    pub(super) fn parse(mut self, content: &str) -> anyhow::Result<Changelog> {
+        let ast = comrak::parse_document(&self.arena, &content, &ComrakOptions::default());
+
+        let mut section_ast = Vec::new();
+        for child in ast.children() {
+            let child_data = child.data.borrow();
+
+            if let NodeValue::Heading(NodeHeading { level: 1, .. }) = child_data.value {
+                if let Some(h1) = self.current_h1.take() {
+                    self.store_version(h1, section_ast)?;
+                }
+
+                self.current_h1 = Some(String::from_utf8(child_data.content.clone())?);
+                section_ast = Vec::new();
+            } else {
+                section_ast.push(child);
+            }
+        }
+        if let Some(h1) = self.current_h1.take() {
+            self.store_version(h1, section_ast)?;
+        }
+
+        Ok(self.result)
+    }
+
+    fn store_version(&mut self, h1: String, body: Vec<&'a AstNode<'a>>) -> anyhow::Result<()> {
+        // Create a document with only the contents of this section
+        let document = self
+            .arena
+            .alloc(AstNode::new(RefCell::new(Ast::new(NodeValue::Document))));
+        for child in &body {
+            document.append(child);
+        }
+
+        let mut content = Vec::new();
+        comrak::format_commonmark(document, &ComrakOptions::default(), &mut content)?;
+        let content = String::from_utf8(content)?;
+
+        if let Some(version) = h1.split(' ').nth(1) {
+            self.result.versions.insert(version.to_string(), content);
+        } else {
+            println!("skipped version, invalid header: {}", h1);
+        }
+
+        Ok(())
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+
+    const CONTENT: &str = "\
+Version 1.45.2 (2020-08-03)
+==========================
+
+* [Fix bindings in tuple struct patterns][74954]
+* [Link in another section][69033]
+
+[74954]: https://github.com/rust-lang/rust/issues/74954
+
+Version 1.45.1 (2020-07-30)
+==========================
+
+* [Fix const propagation with references.][73613]
+* [rustfmt accepts rustfmt_skip in cfg_attr again.][73078]
+
+[73613]: https://github.com/rust-lang/rust/pull/73613
+[73078]: https://github.com/rust-lang/rust/issues/73078
+
+Version 1.44.0 (2020-06-04)
+==========================
+
+Language
+--------
+- [You can now use `async/.await` with `#[no_std]` enabled.][69033]
+
+**Syntax-only changes**
+
+- [Expansion-driven outline module parsing][69838]
+```rust
+#[cfg(FALSE)]
+mod foo {
+    mod bar {
+        mod baz; // `foo/bar/baz.rs` doesn't exist, but no error!
+    }
+}
+```
+
+These are still rejected semantically, so you will likely receive an error but
+these changes can be seen and parsed by macros and conditional compilation.
+
+Internal Only
+-------------
+These changes provide no direct user facing benefits, but represent significant
+improvements to the internals and overall performance of rustc and
+related tools.
+
+- [dep_graph Avoid allocating a set on when the number reads are small.][69778]
+
+[69033]: https://github.com/rust-lang/rust/pull/69033/
+[69838]: https://github.com/rust-lang/rust/pull/69838/
+[69778]: https://github.com/rust-lang/rust/pull/69778/
+";
+
+    const EXPECTED_1_45_2: &str = "\
+- [Fix bindings in tuple struct patterns](https://github.com/rust-lang/rust/issues/74954)
+- [Link in another section](https://github.com/rust-lang/rust/pull/69033/)
+";
+
+    #[test]
+    fn test_changelog_parsing() -> anyhow::Result<()> {
+        let arena = Arena::new();
+        let parsed = RustcFormat::new(&arena).parse(CONTENT)?;
+
+        // Ensure the right markdown is generated from each version
+        let version_1_45_2 = parsed.version("1.45.2").expect("missing version 1.45.2");
+        assert_eq!(EXPECTED_1_45_2, version_1_45_2);
+
+        let version_1_44_0 = parsed.version("1.44.0").expect("missing version 1.44.0");
+        assert!(version_1_44_0.contains("Avoid allocating a set"));
+
+        Ok(())
+    }
+}

+ 1 - 0
src/lib.rs

@@ -10,6 +10,7 @@ use std::fmt;
 
 pub mod actions;
 pub mod agenda;
+mod changelogs;
 pub mod config;
 pub mod db;
 pub mod github;