use codewhale_commands::debug::change::{ count_changelog_entries, extract_changelog_section_by_version, }; use codewhale_localization::{Locale, MessageId, tr}; pub(crate) const BUNDLED_CHANGELOG: &str = include_str!("../CHANGELOG.md"); #[derive(Debug, Clone, PartialEq, Eq)] pub struct WhatsNewNotice { pub version: String, pub updated: String, pub counts: Option, pub notes: String, pub url: String, headline_template: String, } impl WhatsNewNotice { #[must_use] pub fn for_version(locale: Locale, version: &str) -> Self { let section = extract_changelog_section_by_version(BUNDLED_CHANGELOG, version); let counts = section .as_deref() .map(count_changelog_entries) .filter(|counts| counts.features + counts.fixes + counts.other > 0) .map(|counts| { tr(locale, MessageId::UpdateWhatsNewCounts) .replace( "{features}", &count_phrase( locale, counts.features, MessageId::UpdateWhatsNewFeatureSingular, MessageId::UpdateWhatsNewFeaturesPlural, ), ) .replace( "{fixes}", &count_phrase( locale, counts.fixes, MessageId::UpdateWhatsNewFixSingular, MessageId::UpdateWhatsNewFixesPlural, ), ) .replace( "{other}", &count_phrase( locale, counts.other, MessageId::UpdateWhatsNewOtherSingular, MessageId::UpdateWhatsNewOtherPlural, ), ) }); let url = release_notes_url(locale, section.is_some().then_some(version)); Self { version: version.to_string(), updated: tr(locale, MessageId::UpdateWhatsNewUpdated).replace("{version}", version), counts, notes: tr(locale, MessageId::UpdateWhatsNewNotes).replace("{url}", &url), url, headline_template: tr(locale, MessageId::UpdateWhatsNewHeadline).into_owned(), } } #[must_use] pub fn headline(&self) -> String { match &self.counts { Some(counts) => self .headline_template .replace("{updated}", &self.updated) .replace("{counts}", counts), None => self.updated.clone(), } } #[must_use] pub fn notes_with_command(&self) -> String { format!("{} ยท /change {}", self.notes, self.version) } #[must_use] pub fn plain_text(&self) -> String { format!("{}\n{}", self.headline(), self.notes) } } fn count_phrase(locale: Locale, count: usize, one: MessageId, many: MessageId) -> String { if count == 1 { tr(locale, one).into_owned() } else { tr(locale, many).replace("{count}", &count.to_string()) } } #[must_use] pub fn release_notes_url(locale: Locale, version: Option<&str>) -> String { let site_locale = codewhale_command_contract::navigation::public_site_locale_segment(locale.tag()); match version { Some(version) => format!("https://codewhale.net/{site_locale}/changelog/{version}"), None => format!("https://codewhale.net/{site_locale}/changelog"), } } pub(crate) fn take_after_update(locale: Locale, show: bool) -> Option { let home = codewhale_config::codewhale_home().ok()?; let outcome = codewhale_release::record_launch(&home, env!("CARGO_PKG_VERSION")); if let Some(record_error) = outcome.record_error { tracing::debug!(error = %record_error, "could not persist the last-launch record"); } let change = outcome.change.filter(|_| show)?; Some(WhatsNewNotice::for_version(locale, &change.current)) } #[must_use] pub fn plain_cli_notice(update_table: Option<&toml::Value>) -> Option { let show = update_table .cloned() .and_then(|table| { table .try_into::() .ok() }) .unwrap_or_default() .show_whats_new; let settings = codewhale_runtime::settings::Settings::load_read_only().unwrap_or_default(); let locale = codewhale_localization::resolve_locale(&settings.locale); take_after_update(locale, show).map(|notice| notice.plain_text()) }