use crate::change::Change;
use std::str::FromStr;
#[derive(Debug, Clone, Default)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Release {
pub(crate) summary: Option<String>,
pub(crate) version: Option<semver::Version>,
pub(crate) added: Vec<Change>,
pub(crate) changed: Vec<Change>,
pub(crate) deprecated: Vec<Change>,
pub(crate) removed: Vec<Change>,
pub(crate) fixed: Vec<Change>,
pub(crate) security: Vec<Change>,
yanked: bool,
#[cfg(feature = "dates")]
date: Option<chrono::NaiveDate>,
}
impl FromStr for Release {
type Err = semver::Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(Self {
version: Some(s.parse()?),
..Self::default()
})
}
}
impl Release {
pub(crate) fn format_markdown_title(&self) -> String {
let mut parts = Vec::new();
parts.push(match self.version.as_ref() {
None => "Unreleased".to_string(),
Some(version) => version.to_string(),
});
#[cfg(feature = "dates")]
if let Some(date) = self.date.as_ref() {
parts.push(date.to_string())
}
if self.yanked {
parts.push("[YANKED]".to_string())
}
parts.join(" - ")
}
pub fn unreleased() -> Self {
Self {
version: None,
..Default::default()
}
}
pub fn new(major: u64, minor: u64, patch: u64) -> Self {
Self {
version: Some(semver::Version::new(major, minor, patch)),
..Default::default()
}
}
pub fn with_summary(self, summary: impl ToString) -> Self {
Self {
summary: Some(summary.to_string()),
..self
}
}
#[cfg(feature = "dates")]
pub fn with_date(self, year: i32, month: u32, day: u32) -> Self {
Self {
date: Some(chrono::NaiveDate::from_ymd_opt(year, month, day).unwrap()),
..self
}
}
pub fn added(self, change: impl Into<Change>) -> Self {
Self {
added: crate::appended_list(self.added, change.into()),
..self
}
}
pub fn changed(self, change: impl Into<Change>) -> Self {
Self {
changed: crate::appended_list(self.changed, change.into()),
..self
}
}
pub fn deprecated(self, change: impl Into<Change>) -> Self {
Self {
deprecated: crate::appended_list(self.deprecated, change.into()),
..self
}
}
pub fn removed(self, change: impl Into<Change>) -> Self {
Self {
removed: crate::appended_list(self.removed, change.into()),
..self
}
}
pub fn fixed(self, change: impl Into<Change>) -> Self {
Self {
fixed: crate::appended_list(self.fixed, change.into()),
..self
}
}
pub fn security(self, change: impl Into<Change>) -> Self {
Self {
security: crate::appended_list(self.security, change.into()),
..self
}
}
pub fn yank(self) -> Self {
Self {
yanked: true,
..self
}
}
pub(crate) fn changes(&self) -> Vec<(&'static str, &[Change])> {
[
("Added", self.added.as_slice()),
("Changed", self.changed.as_slice()),
("Deprecated", self.deprecated.as_slice()),
("Removed", self.removed.as_slice()),
("Fixed", self.fixed.as_slice()),
("Security", self.security.as_slice()),
]
.into_iter()
.filter(|(_, c)| !c.is_empty())
.collect()
}
}