gitcc_changelog/
lib.rs

1//! Changelog
2//!
3//! The changelog format is defined in https://keepachangelog.com/en/1.0.0/.
4
5use handlebars::Handlebars;
6use serde::{Serialize, Serializer};
7use time::{macros::format_description, OffsetDateTime};
8
9/// Base changelog template
10pub const TEMPLATE_CHANGELOG_STD: &str = include_str!("tpl/changelog.hbs");
11
12/// Base release template
13pub const TEMPLATE_RELEASE_STD: &str = include_str!("tpl/release.hbs");
14
15/// Changelog
16///
17/// The generic `T` is the "form" of the commit
18#[derive(Debug, Serialize)]
19pub struct Changelog {
20    /// Releases (latest first)
21    pub releases: Vec<Release>,
22}
23
24/// Changelog release
25#[derive(Debug, Serialize)]
26pub struct Release {
27    /// Version (v0.0.1, Unreleased, ...)
28    pub version: String,
29    /// Release date
30    #[serde(serialize_with = "serialize_date")]
31    pub date: OffsetDateTime,
32    /// A link to the release
33    ///
34    /// [Unreleased]: https://github.com/olivierlacan/keep-a-changelog/compare/v1.0.0...HEAD
35    /// [1.0.0]: https://github.com/olivierlacan/keep-a-changelog/compare/v0.0.2...v0.0.1
36    pub url: Option<String>,
37    /// Sections
38    pub sections: Vec<Section>,
39}
40
41/// Serializes a date
42fn serialize_date<S>(date: &OffsetDateTime, serializer: S) -> Result<S::Ok, S::Error>
43where
44    S: Serializer,
45{
46    let format = format_description!("[year]-[month]-[day]");
47    date.format(&format).unwrap().serialize(serializer)
48}
49
50/// Changelog release section
51#[derive(Debug, Serialize)]
52pub struct Section {
53    /// Section label
54    pub label: String,
55    /// Section items
56    pub items: Vec<String>,
57}
58
59impl PartialEq for Section {
60    fn eq(&self, other: &Self) -> bool {
61        self.label == other.label
62    }
63}
64
65impl Section {
66    /// Initializes a new section
67    pub fn new(label: &str) -> Self {
68        Self {
69            label: label.to_string(),
70            items: vec![],
71        }
72    }
73}
74
75/// Changelog error
76#[derive(Debug, thiserror::Error)]
77#[error("Changelog error:{0}")]
78pub struct Error(String);
79
80impl Changelog {
81    /// Generates the change log
82    pub fn render(&self, template: &str) -> Result<String, Error> {
83        let handlebars = Handlebars::new();
84        handlebars
85            .render_template(template, &self)
86            .map_err(|err| Error(err.to_string()))
87    }
88}
89
90impl Release {
91    /// Generates the release note
92    pub fn render(&self, template: &str) -> Result<String, Error> {
93        let handlebars = Handlebars::new();
94        handlebars
95            .render_template(template, &self)
96            .map_err(|err| Error(err.to_string()))
97    }
98}