Unified · Split
| @@ -6,7 +6,7 @@ mod init; · 5 unmodified lines | |||
| 6 | mod merge; | 6 | mod merge; |
| 7 | mod parse; | 7 | mod parse; |
| 8 | pub mod section; | 8 | pub mod section; |
| 9 | mod write; | 9 | pub mod write; |
| 10 | 10 | ||
| 11 | #[derive(PartialEq, Eq, Debug, Clone)] | 11 | #[derive(PartialEq, Eq, Debug, Clone)] |
| 12 | pub enum Section { | 12 | pub enum Section { |
| @@ -19,12 +19,24 @@ impl std::fmt::Display for changelog::Version { · 18 unmodified lines | |||
| 19 | } | 19 | } |
| 20 | } | 20 | } |
| 21 | 21 | ||
| 22 | /// Define how linkable items should be written | ||
| 23 | #[derive(Clone)] | ||
| 24 | pub enum Linkables { | ||
| 25 | /// Use markdown links to link directly to the linkable items | ||
| 26 | AsLinks { | ||
| 27 | /// The location of the repository to link to, without trailing slash | ||
| 28 | repository_url: String, | ||
| 29 | }, | ||
| 30 | /// Leave them in a textual representation for the hosting platform to auto-link them | ||
| 31 | AsText, | ||
| 32 | } | ||
| 33 | |||
| 22 | impl Section { | 34 | impl Section { |
| 23 | pub const UNKNOWN_TAG_START: &'static str = "<csr-unknown>"; | 35 | pub const UNKNOWN_TAG_START: &'static str = "<csr-unknown>"; |
| 24 | pub const UNKNOWN_TAG_END: &'static str = "<csr-unknown/>"; | 36 | pub const UNKNOWN_TAG_END: &'static str = "<csr-unknown/>"; |
| 25 | pub const READONLY_TAG: &'static str = "<csr-read-only-do-not-edit/>\n"; // needs a newline to not interfere with formatting | 37 | pub const READONLY_TAG: &'static str = "<csr-read-only-do-not-edit/>\n"; // needs a newline to not interfere with formatting |
| 26 | 38 | ||
| 27 | pub fn write_to(&self, mut out: impl std::io::Write) -> std::io::Result<()> { | 39 | pub fn write_to(&self, mut out: impl std::io::Write, link_mode: &Linkables) -> std::io::Result<()> { |
| 28 | match self { | 40 | match self { |
| 29 | Section::Verbatim { text, .. } => out.write_all(text.as_bytes()), | 41 | Section::Verbatim { text, .. } => out.write_all(text.as_bytes()), |
| 30 | Section::Release { | 42 | Section::Release { |
| @@ -55,7 +67,7 @@ impl Section { · 24 unmodified lines | |||
| 55 | 67 | ||
| 56 | let section_level = *heading_level + 1; | 68 | let section_level = *heading_level + 1; |
| 57 | for segment in segments { | 69 | for segment in segments { |
| 58 | segment.write_to(section_level, &mut out)?; | 70 | segment.write_to(section_level, link_mode, &mut out)?; |
| 59 | } | 71 | } |
| 60 | if !unknown.is_empty() { | 72 | if !unknown.is_empty() { |
| 61 | writeln!(out, "{}", Section::UNKNOWN_TAG_START)?; | 73 | writeln!(out, "{}", Section::UNKNOWN_TAG_START)?; |
| @@ -73,16 +85,21 @@ fn heading(level: usize) -> String { · 11 unmodified lines | |||
| 73 | } | 85 | } |
| 74 | 86 | ||
| 75 | impl ChangeLog { | 87 | impl ChangeLog { |
| 76 | pub fn write_to(&self, mut out: impl std::io::Write) -> std::io::Result<()> { | 88 | pub fn write_to(&self, mut out: impl std::io::Write, link_mode: &Linkables) -> std::io::Result<()> { |
| 77 | for section in &self.sections { | 89 | for section in &self.sections { |
| 78 | section.write_to(&mut out)?; | 90 | section.write_to(&mut out, link_mode)?; |
| 79 | } | 91 | } |
| 80 | Ok(()) | 92 | Ok(()) |
| 81 | } | 93 | } |
| 82 | } | 94 | } |
| 83 | 95 | ||
| 84 | impl section::Segment { | 96 | impl section::Segment { |
| 85 | pub fn write_to(&self, section_level: usize, mut out: impl std::io::Write) -> std::io::Result<()> { | 97 | pub fn write_to( |
| 98 | &self, | ||
| 99 | section_level: usize, | ||
| 100 | _link_mode: &Linkables, | ||
| 101 | mut out: impl std::io::Write, | ||
| 102 | ) -> std::io::Result<()> { | ||
| 86 | match self { | 103 | match self { |
| 87 | Segment::User { markdown } => out.write_all(markdown.as_bytes())?, | 104 | Segment::User { markdown } => out.write_all(markdown.as_bytes())?, |
| 88 | Segment::Conventional(segment::Conventional { | 105 | Segment::Conventional(segment::Conventional { |
| @@ -1,3 +1,4 @@ | |||
| 1 | use crate::changelog::write::Linkables; | ||
| 1 | use crate::{bat, command::changelog::Options, git, utils::will, ChangeLog}; | 2 | use crate::{bat, command::changelog::Options, git, utils::will, ChangeLog}; |
| 2 | 3 | ||
| 3 | pub fn changelog(opts: Options, crates: Vec<String>) -> anyhow::Result<()> { | 4 | pub fn changelog(opts: Options, crates: Vec<String>) -> anyhow::Result<()> { |
| @@ -33,7 +34,16 @@ pub fn changelog(opts: Options, crates: Vec<String>) -> anyhow::Result<()> { · 29 unmodified lines | |||
| 33 | .expect("contained in workspace") | 34 | .expect("contained in workspace") |
| 34 | .display() | 35 | .display() |
| 35 | ); | 36 | ); |
| 36 | lock.with_mut(|file| log.write_to(file))?; | 37 | lock.with_mut(|file| { |
| 38 | log.write_to( | ||
| 39 | file, | ||
| 40 | &if opts.dry_run { | ||
| 41 | Linkables::AsText | ||
| 42 | } else { | ||
| 43 | Linkables::AsText // TODO: figure out repo url | ||
| 44 | }, | ||
| 45 | ) | ||
| 46 | })?; | ||
| 37 | if let Some(bat) = bat.as_ref() { | 47 | if let Some(bat) = bat.as_ref() { |
| 38 | bat.display_to_tty(lock.lock_path())?; | 48 | bat.display_to_tty(lock.lock_path())?; |
| 39 | } | 49 | } |
| @@ -86,7 +86,7 @@ pub(in crate::command::release_impl) fn edit_version_and_fixup_dependent_crates_ · 85 unmodified lines | |||
| 86 | if !recent_release_in_log.is_essential() { | 86 | if !recent_release_in_log.is_essential() { |
| 87 | empty_changelogs_for_current_version.push(pending_changelog_changes.len()); | 87 | empty_changelogs_for_current_version.push(pending_changelog_changes.len()); |
| 88 | } | 88 | } |
| 89 | lock.with_mut(|file| log.write_to(file))?; | 89 | lock.with_mut(|file| log.write_to(file, &changelog::write::Linkables::AsText))?; // TODO: use repo url |
| 90 | pending_changelog_changes.push((publishee, changed_relevant_content, lock)); | 90 | pending_changelog_changes.push((publishee, changed_relevant_content, lock)); |
| 91 | } | 91 | } |
| 92 | } | 92 | } |
| @@ -45,21 +45,28 @@ fn conventional_write_empty_messages() -> Result { · 44 unmodified lines | |||
| 45 | }], | 45 | }], |
| 46 | }; | 46 | }; |
| 47 | 47 | ||
| 48 | for _round in 1..=2 { | 48 | for link_mode in &[ |
| 49 | let mut buf = Vec::<u8>::new(); | 49 | changelog::write::Linkables::AsText, |
| 50 | log.write_to(&mut buf)?; | 50 | changelog::write::Linkables::AsLinks { |
| 51 | let md = buf.to_str()?; | 51 | repository_url: "https://github.com/user/repo".into(), |
| 52 | insta::assert_snapshot!(md); | 52 | }, |
| 53 | ] { | ||
| 54 | for _round in 1..=2 { | ||
| 55 | let mut buf = Vec::<u8>::new(); | ||
| 56 | log.write_to(&mut buf, link_mode)?; | ||
| 57 | let md = buf.to_str()?; | ||
| 58 | insta::assert_snapshot!(md); | ||
| 53 | 59 | ||
| 54 | let parsed_log = ChangeLog::from_markdown(md); | 60 | let parsed_log = ChangeLog::from_markdown(md); |
| 55 | assert_eq!(parsed_log, log, "we can parse this back losslessly"); | 61 | assert_eq!(parsed_log, log, "we can parse this back losslessly"); |
| 62 | } | ||
| 56 | } | 63 | } |
| 57 | Ok(()) | 64 | Ok(()) |
| 58 | } | 65 | } |
| 59 | 66 | ||
| 60 | #[test] | 67 | #[test] |
| 61 | fn all_section_types_round_trips_lossy() -> Result { | 68 | fn all_section_types_round_trips_lossy() -> Result { |
| 62 | let mut log = ChangeLog { | 69 | let log = ChangeLog { |
| 63 | sections: vec![ | 70 | sections: vec![ |
| 64 | Section::Verbatim { | 71 | Section::Verbatim { |
| 65 | text: "# Changelog\n\nmy very own header\n\n".into(), | 72 | text: "# Changelog\n\nmy very own header\n\n".into(), |
| @@ -130,16 +137,21 @@ fn all_section_types_round_trips_lossy() -> Result { · 64 unmodified lines | |||
| 130 | }, | 137 | }, |
| 131 | ], | 138 | ], |
| 132 | }; | 139 | }; |
| 133 | for _round in 0..1 { | 140 | |
| 141 | for link_mode in &[ | ||
| 142 | changelog::write::Linkables::AsText, | ||
| 143 | changelog::write::Linkables::AsLinks { | ||
| 144 | repository_url: "https://github.com/user/repo".into(), | ||
| 145 | }, | ||
| 146 | ] { | ||
| 134 | // NOTE: we can't run this a second time as the statistical information will be gone (it was never parsed back) | 147 | // NOTE: we can't run this a second time as the statistical information will be gone (it was never parsed back) |
| 135 | let mut buf = Vec::<u8>::new(); | 148 | let mut buf = Vec::<u8>::new(); |
| 136 | log.write_to(&mut buf)?; | 149 | log.write_to(&mut buf, link_mode)?; |
| 137 | let md = buf.to_str()?; | 150 | let md = buf.to_str()?; |
| 138 | insta::assert_snapshot!(md); | 151 | insta::assert_snapshot!(md); |
| 139 | 152 | ||
| 140 | let parsed_log = ChangeLog::from_markdown(md); | 153 | let parsed_log = ChangeLog::from_markdown(md); |
| 141 | assert_eq!(parsed_log, log, "we must be able to parse the exact input back"); | 154 | assert_eq!(parsed_log, log, "we must be able to parse the exact input back"); |
| 142 | log = parsed_log; | ||
| 143 | } | 155 | } |
| 144 | Ok(()) | 156 | Ok(()) |
| 145 | } | 157 | } |
| @@ -0,0 +1,48 @@ | |||
| 1 | --- | ||
| 2 | source: cargo-smart-release/tests/changelog/write_and_parse/mod.rs | ||
| 3 | expression: md | ||
| 4 | |||
| 5 | --- | ||
| 6 | # Changelog | ||
| 7 | |||
| 8 | my very own header | ||
| 9 | |||
| 10 | ## Unreleased | ||
| 11 | |||
| 12 | <csr-unknown> | ||
| 13 | hello | ||
| 14 | world | ||
| 15 | <csr-unknown/> | ||
| 16 | #### v1.0.2-beta.2 (1970-01-01) | ||
| 17 | |||
| 18 | * hello world | ||
| 19 | this | ||
| 20 | ##### Thanks Clippy | ||
| 21 | |||
| 22 | <csr-read-only-do-not-edit/> | ||
| 23 | |||
| 24 | [Clippy](https://github.com/rust-lang/rust-clippy) helped 42 times to make code idiomatic. | ||
| 25 | |||
| 26 | ##### Commit Statistics | ||
| 27 | |||
| 28 | <csr-read-only-do-not-edit/> | ||
| 29 | |||
| 30 | - 100 commits contributed to the release over the course of 32 calendar days. | ||
| 31 | - 20 commits where understood as [conventional](https://www.conventionalcommits.org). | ||
| 32 | - 3 unique issues were worked on: #1, Uncategorized, #42 | ||
| 33 | |||
| 34 | ##### Commit Details | ||
| 35 | |||
| 36 | <csr-read-only-do-not-edit/> | ||
| 37 | |||
| 38 | <details><summary>view details</summary> | ||
| 39 | |||
| 40 | * **#42** | ||
| 41 | - Just the title (e69de29) | ||
| 42 | - another title (e69de29) | ||
| 43 | * **Uncategorized** | ||
| 44 | - Just the title (e69de29) | ||
| 45 | - title and body (e69de29) | ||
| 46 | </details> | ||
| 47 | |||
| 48 | |||
| @@ -0,0 +1,25 @@ | |||
| 1 | --- | ||
| 2 | source: cargo-smart-release/tests/changelog/write_and_parse/mod.rs | ||
| 3 | expression: md | ||
| 4 | |||
| 5 | --- | ||
| 6 | #### v1.0.2-beta.2 (1970-01-01) | ||
| 7 | |||
| 8 | <csr-id-0000000000000000000000000000000000000002/> | ||
| 9 | |||
| 10 | ##### New Features (BREAKING) | ||
| 11 | |||
| 12 | <csr-id-0000000000000000000000000000000000000001/> | ||
| 13 | |||
| 14 | - verbatim `whatever` the _user_ writes [hello](world) | ||
| 15 | - <csr-id-0000000000000000000000000000000000000003/> this messages comes straight from git conventional and _may_ contain markdown | ||
| 16 | first line | ||
| 17 | second line | ||
| 18 | |||
| 19 | another paragraph | ||
| 20 | - just another user message, this time | ||
| 21 | with multiple lines | ||
| 22 | |||
| 23 | and a new paragraph | ||
| 24 | |||
| 25 | |||
| @@ -0,0 +1,25 @@ | |||
| 1 | --- | ||
| 2 | source: cargo-smart-release/tests/changelog/write_and_parse/mod.rs | ||
| 3 | expression: md | ||
| 4 | |||
| 5 | --- | ||
| 6 | #### v1.0.2-beta.2 (1970-01-01) | ||
| 7 | |||
| 8 | <csr-id-0000000000000000000000000000000000000002/> | ||
| 9 | |||
| 10 | ##### New Features (BREAKING) | ||
| 11 | |||
| 12 | <csr-id-0000000000000000000000000000000000000001/> | ||
| 13 | |||
| 14 | - verbatim `whatever` the _user_ writes [hello](world) | ||
| 15 | - <csr-id-0000000000000000000000000000000000000003/> this messages comes straight from git conventional and _may_ contain markdown | ||
| 16 | first line | ||
| 17 | second line | ||
| 18 | |||
| 19 | another paragraph | ||
| 20 | - just another user message, this time | ||
| 21 | with multiple lines | ||
| 22 | |||
| 23 | and a new paragraph | ||
| 24 | |||
| 25 | |||