1use handlebars::Handlebars;
6use serde::{Serialize, Serializer};
7use time::{macros::format_description, OffsetDateTime};
8
9pub const TEMPLATE_CHANGELOG_STD: &str = include_str!("tpl/changelog.hbs");
11
12pub const TEMPLATE_RELEASE_STD: &str = include_str!("tpl/release.hbs");
14
15#[derive(Debug, Serialize)]
19pub struct Changelog {
20 pub releases: Vec<Release>,
22}
23
24#[derive(Debug, Serialize)]
26pub struct Release {
27 pub version: String,
29 #[serde(serialize_with = "serialize_date")]
31 pub date: OffsetDateTime,
32 pub url: Option<String>,
37 pub sections: Vec<Section>,
39}
40
41fn 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#[derive(Debug, Serialize)]
52pub struct Section {
53 pub label: String,
55 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 pub fn new(label: &str) -> Self {
68 Self {
69 label: label.to_string(),
70 items: vec![],
71 }
72 }
73}
74
75#[derive(Debug, thiserror::Error)]
77#[error("Changelog error:{0}")]
78pub struct Error(String);
79
80impl Changelog {
81 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 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}