diff --git a/src/app.rs b/src/app.rs index 6f89c52..f7407c2 100644 --- a/src/app.rs +++ b/src/app.rs @@ -47,8 +47,12 @@ impl App { } else { None }; - let changelog_entry = - generate_changelog_entry(&commits, &new_version.to_string(), version_description); + let changelog_entry = generate_changelog_entry( + &repo, + &commits, + &new_version.to_string(), + version_description, + )?; let replacement = VersionReplacement { old_version: tag.version.to_string(), @@ -88,13 +92,14 @@ impl App { let commits = get_commits_between_tags(&repo, &version_range[1], &version_range[0])?; let changelog_entry = - generate_changelog_entry(&commits, &version.to_string(), None); + generate_changelog_entry(&repo, &commits, &version.to_string(), None)?; println!("{}", changelog_entry); } None => { let tag = get_latest_tag(&repo)?; let commits = get_commits_since_tag(&repo, &tag)?; - let changelog_entry = generate_changelog_entry(&commits, "unreleased", None); + let changelog_entry = + generate_changelog_entry(&repo, &commits, "unreleased", None)?; let path = std::path::PathBuf::from("CHANGELOG.md"); if opts.no_decorations { match opts.only_current_version { diff --git a/src/changelog.rs b/src/changelog.rs index 535875a..5415ee3 100644 --- a/src/changelog.rs +++ b/src/changelog.rs @@ -1,4 +1,4 @@ -use crate::versioning::Commit; +use crate::{error::Result, versioning::Commit}; use conventional_commit_parser::commit::CommitType; use std::collections::HashMap; @@ -11,7 +11,7 @@ pub struct ChangelogEntry<'a> { pub description: Option, } -#[derive(Clone, Debug, serde::Serialize)] +#[derive(Clone, Debug)] pub struct ChangelogCommit { pub scope: Option, pub summary: String, @@ -56,13 +56,20 @@ pub fn display_commit_type(commit_type: &CommitType) -> String { } pub fn generate_changelog_entry<'a, I: IntoIterator>( + repo: &gix::Repository, commits: I, version: &str, description: Option, -) -> String { +) -> Result { let mut env = minijinja::Environment::new(); env.add_template("changelog_entry", TEMPLATE).unwrap(); + let url = gix_repo_url(repo)?; + let version = match &url { + Some(url) => &format!("[{version}]({url}/releases/tag/{version})"), + None => version, + }; + let typed_commits: HashMap> = commits.into_iter().fold(HashMap::new(), |mut acc, commit| { let key = display_commit_type(&commit.conventional_commit.commit_type); @@ -87,5 +94,24 @@ pub fn generate_changelog_entry<'a, I: IntoIterator>( .render(minijinja::context!( entry => entry, )) - .unwrap() + .map_err(Into::into) +} + +fn gix_repo_url(repo: &gix::Repository) -> Result> { + let remote = match repo.find_default_remote(gix::remote::Direction::Push) { + Some(remote) => remote?, + None => return Ok(None), + }; + + match remote.url(gix::remote::Direction::Push) { + Some(url) => { + let host = url.host_argument_safe(); + let path = url.path_argument_safe(); + match (host, path) { + (Some(host), Some(path)) => Ok(Some(format!("https://{host}/{path}"))), + _ => Ok(None), + } + } + None => Ok(None), + } } diff --git a/src/error.rs b/src/error.rs index 0b8e5f4..46517da 100644 --- a/src/error.rs +++ b/src/error.rs @@ -41,15 +41,21 @@ pub enum Error { #[error(transparent)] GixReferenceHeadId(#[from] gix::reference::head_id::Error), #[error(transparent)] + GixRemoteFindExisting(#[from] gix::remote::find::existing::Error), + #[error(transparent)] GixHeadCommit(#[from] gix::reference::head_commit::Error), #[error(transparent)] GixWalk(#[from] gix::revision::walk::Error), #[error(transparent)] ConventialCommitParse(#[from] conventional_commit_parser::error::ParseError), #[error(transparent)] + MiniJinja(#[from] minijinja::Error), + #[error(transparent)] Other(#[from] anyhow::Error), #[error(transparent)] StdPathStripPrefix(#[from] std::path::StripPrefixError), + #[error(transparent)] + StdStrUtf8Error(#[from] std::str::Utf8Error), #[error("invalid version: {0}")] TomlSerialize(#[from] toml::ser::Error), #[error("invalid toml: {0}")]