use governor_core::domain::{
commit::{Commit, CommitType},
version::{BreakingChange, Feature, Fix, MigrationComplexity},
};
#[derive(Debug, Default)]
pub struct Analysis {
pub breaking: Vec<BreakingCommit>,
pub features: Vec<FeatureCommit>,
pub fixes: Vec<FixCommit>,
pub other: usize,
}
#[derive(Debug, Clone)]
pub struct BreakingCommit {
pub hash: String,
pub short_hash: String,
pub message: String,
pub description: Option<String>,
pub has_exclamation: bool,
}
#[derive(Debug, Clone)]
pub struct FeatureCommit {
pub hash: String,
pub short_hash: String,
pub message: String,
pub scope: Option<String>,
}
#[derive(Debug, Clone)]
pub struct FixCommit {
pub hash: String,
pub short_hash: String,
pub message: String,
pub scope: Option<String>,
}
#[must_use]
pub fn analyze_commits(commits: &[Commit]) -> Analysis {
let mut analysis = Analysis::default();
for commit in commits {
let short_hash = commit.hash.chars().take(7).collect::<String>();
match commit.commit_type {
Some(CommitType::Feat) => {
if commit.breaking {
analysis.breaking.push(BreakingCommit {
hash: commit.hash.clone(),
short_hash,
message: commit.short_message().to_string(),
description: Some("Breaking feature change".to_string()),
has_exclamation: commit.message.contains('!'),
});
} else {
analysis.features.push(FeatureCommit {
hash: commit.hash.clone(),
short_hash,
message: commit.short_message().to_string(),
scope: commit.scope.clone(),
});
}
}
Some(CommitType::Fix) => {
if commit.breaking {
analysis.breaking.push(BreakingCommit {
hash: commit.hash.clone(),
short_hash,
message: commit.short_message().to_string(),
description: Some("Breaking fix".to_string()),
has_exclamation: commit.message.contains('!'),
});
} else {
analysis.fixes.push(FixCommit {
hash: commit.hash.clone(),
short_hash,
message: commit.short_message().to_string(),
scope: commit.scope.clone(),
});
}
}
Some(CommitType::Perf | CommitType::Refactor) => {
if commit.breaking {
analysis.breaking.push(BreakingCommit {
hash: commit.hash.clone(),
short_hash,
message: commit.short_message().to_string(),
description: Some("Breaking refactor/perf change".to_string()),
has_exclamation: commit.message.contains('!'),
});
}
}
_ => {
analysis.other += 1;
}
}
}
analysis
}
#[must_use]
pub fn build_breaking_changes(
breaking: Vec<BreakingCommit>,
package_name: &str,
) -> Vec<BreakingChange> {
breaking
.into_iter()
.map(|c| BreakingChange {
commit_hash: c.hash.clone(),
short_hash: c.short_hash.clone(),
message: c.message.clone(),
breaking_description: c
.description
.unwrap_or_else(|| "Breaking change detected".to_string()),
affected_crates: vec![package_name.to_string()],
migration_complexity: if c.has_exclamation {
MigrationComplexity::Complex
} else {
MigrationComplexity::Simple
},
})
.collect()
}
#[must_use]
pub fn build_features(features: Vec<FeatureCommit>, _package_name: &str) -> Vec<Feature> {
features
.into_iter()
.map(|c| Feature {
commit_hash: c.hash.clone(),
short_hash: c.short_hash.clone(),
message: c.message.clone(),
scope: c.scope,
affected_crates: Vec::new(),
})
.collect()
}
#[must_use]
pub fn build_fixes(fixes: Vec<FixCommit>, _package_name: &str) -> Vec<Fix> {
fixes
.into_iter()
.map(|c| Fix {
commit_hash: c.hash.clone(),
short_hash: c.short_hash.clone(),
message: c.message.clone(),
scope: c.scope,
affected_crates: Vec::new(),
})
.collect()
}