commit_wizard/engine/capabilities/versioning/
mod.rs1use crate::engine::capabilities::commit::check::ParsedHeader;
2use crate::engine::models::{
3 git::CommitSummary,
4 policy::{commit::CommitModel, enforcement::BumpLevel, versioning::Version},
5};
6
7pub fn classify_commits(
13 commits: &[CommitSummary],
14 policy: &CommitModel,
15) -> Vec<(CommitSummary, BumpLevel)> {
16 commits
17 .iter()
18 .map(|commit| {
19 let bump = classify_single_commit(commit, policy);
20 (commit.clone(), bump)
21 })
22 .collect()
23}
24
25fn classify_single_commit(commit: &CommitSummary, policy: &CommitModel) -> BumpLevel {
27 let header = &commit.summary;
29
30 if let Some(parsed) = ParsedHeader::parse(header) {
32 let is_breaking_footer = commit
34 .full_message
35 .as_ref()
36 .map(|msg| msg.contains(&format!("{}:", policy.breaking_footer_key)))
37 .unwrap_or(false);
38
39 if parsed.is_breaking || is_breaking_footer {
40 return BumpLevel::Major;
41 }
42
43 if let Some(type_model) = policy.find_type(&parsed.type_name) {
45 return type_model.bump;
46 }
47
48 return BumpLevel::None;
50 }
51
52 BumpLevel::None
54}
55
56pub fn calculate_next_version(
64 current_version: Option<Version>,
65 classified_commits: &[(CommitSummary, BumpLevel)],
66) -> Version {
67 let highest_bump = classified_commits
69 .iter()
70 .map(|(_, bump)| bump)
71 .max_by_key(|bump| match bump {
72 BumpLevel::Major => 3,
73 BumpLevel::Minor => 2,
74 BumpLevel::Patch => 1,
75 BumpLevel::None => 0,
76 })
77 .copied()
78 .unwrap_or(BumpLevel::None);
79
80 let base_version = current_version.unwrap_or(Version {
82 major: 0,
83 minor: 1,
84 patch: 0,
85 });
86
87 base_version.with_bump(highest_bump)
89}