Skip to main content

commit_wizard/engine/models/policy/
changelog.rs

1use crate::engine::models::{policy::enforcement::ChangelogFormat, runtime::ResolvedConfig};
2use std::collections::BTreeMap;
3
4#[derive(Debug, Clone)]
5pub struct ChangelogHeaderModel {
6    pub use_header: bool,
7    pub title: String,
8    pub description: Option<String>,
9}
10
11#[derive(Debug, Clone)]
12pub struct ChangelogSectionModel {
13    pub title: Option<String>,
14    pub order: Option<i32>,
15}
16
17#[derive(Debug, Clone, Copy, PartialEq, Eq)]
18pub enum ChangelogOutputFormat {
19    Markdown,
20    Json,
21}
22
23impl From<ChangelogFormat> for ChangelogOutputFormat {
24    fn from(value: ChangelogFormat) -> Self {
25        match value {
26            ChangelogFormat::Markdown => Self::Markdown,
27            ChangelogFormat::Json => Self::Json,
28        }
29    }
30}
31
32#[derive(Debug, Clone)]
33pub struct ChangelogModel {
34    pub output: String,
35    pub format: ChangelogOutputFormat,
36    pub header: ChangelogHeaderModel,
37    pub group_by: Vec<String>,
38    pub section_order: Vec<String>,
39    pub scope_order: Vec<String>,
40    pub show_scope: bool,
41    pub show_empty_sections: Option<bool>,
42    pub show_empty_scopes: Option<bool>,
43    pub misc_section: Option<String>,
44    pub unreleased_label: String,
45    pub date_format: Option<String>,
46    pub sections: BTreeMap<String, ChangelogSectionModel>,
47}
48
49impl Default for ChangelogModel {
50    fn default() -> Self {
51        Self {
52            output: "CHANGELOG.md".to_string(),
53            format: ChangelogOutputFormat::Markdown,
54            header: ChangelogHeaderModel {
55                use_header: true,
56                title: "Changelog".to_string(),
57                description: None,
58            },
59            group_by: vec!["type".to_string()],
60            section_order: vec![
61                "feat".to_string(),
62                "fix".to_string(),
63                "docs".to_string(),
64                "style".to_string(),
65                "refactor".to_string(),
66                "perf".to_string(),
67                "test".to_string(),
68                "chore".to_string(),
69            ],
70            scope_order: Vec::new(),
71            show_scope: true,
72            show_empty_sections: Some(false),
73            show_empty_scopes: Some(false),
74            misc_section: Some("Miscellaneous".to_string()),
75            unreleased_label: "Unreleased".to_string(),
76            date_format: None,
77            sections: BTreeMap::new(),
78        }
79    }
80}
81
82impl ChangelogModel {
83    pub fn from_config(config: &ResolvedConfig) -> Self {
84        let base = &config.base;
85
86        // Load sections from config, converting ChangelogSectionConfig to ChangelogSectionModel
87        let sections_from_config = base.changelog_sections();
88        let sections = sections_from_config
89            .into_iter()
90            .map(|(key, cfg)| {
91                (
92                    key,
93                    ChangelogSectionModel {
94                        title: cfg.title,
95                        order: cfg.order,
96                    },
97                )
98            })
99            .collect();
100
101        Self {
102            output: base.changelog_output(),
103            format: base.changelog_format().into(),
104            header: ChangelogHeaderModel {
105                use_header: base.changelog_header_use(),
106                title: base.changelog_header_title(),
107                description: base.changelog_header_description(),
108            },
109            group_by: base.changelog_group_by(),
110            section_order: base.changelog_section_order(),
111            scope_order: base.changelog_scope_order(),
112            show_scope: base.changelog_show_scope(),
113            show_empty_sections: base.changelog_show_empty_sections(),
114            show_empty_scopes: base.changelog_show_empty_scopes(),
115            misc_section: base.changelog_misc_section(),
116            unreleased_label: base.changelog_unreleased_label(),
117            date_format: base.changelog_date_format(),
118            sections,
119        }
120    }
121}
122
123pub fn section_for_type<'a>(sections: &'a BTreeMap<String, Vec<String>>, typ: &'a str) -> &'a str {
124    for (section, types) in sections {
125        if types.iter().any(|t| t == typ) {
126            return section;
127        }
128    }
129    "Uncategorized"
130}