This commit is contained in:
107
src/output.rs
Normal file
107
src/output.rs
Normal file
@@ -0,0 +1,107 @@
|
||||
use serde::Serialize;
|
||||
use crate::convention::CommitSuggestion;
|
||||
use crate::analyzer::ChangedFile;
|
||||
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
pub enum OutputFormat { Short, Verbose, Json, Compact }
|
||||
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
pub struct JsonOutput {
|
||||
pub prefix: String, pub commit_type: String, pub scope: Option<String>,
|
||||
pub description: String, pub confidence: f64, pub file_count: usize,
|
||||
pub files: Vec<JsonFileChange>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
pub struct JsonFileChange { pub path: String, pub status: String, pub additions: usize, pub deletions: usize; }
|
||||
|
||||
pub trait OutputFormatter { fn format(&self, suggestion: &CommitSuggestion, files: &[ChangedFile]) -> String; }
|
||||
|
||||
pub struct ShortFormatter;
|
||||
impl OutputFormatter for ShortFormatter {
|
||||
fn format(&self, suggestion: &CommitSuggestion, _files: &[ChangedFile]) -> String {
|
||||
match &suggestion.scope {
|
||||
Some(scope) => format!("{}({}): {}", suggestion.commit_type, scope, suggestion.description),
|
||||
None => format!("{}: {}", suggestion.commit_type, suggestion.description),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct VerboseFormatter;
|
||||
impl OutputFormatter for VerboseFormatter {
|
||||
fn format(&self, suggestion: &CommitSuggestion, files: &[ChangedFile]) -> String {
|
||||
let mut output = String::new();
|
||||
output.push_str(&format!("Commit Type: {}\n", suggestion.commit_type));
|
||||
if let Some(scope) = &suggestion.scope { output.push_str(&format!("Scope: {}\n", scope)); }
|
||||
output.push_str(&format!("Description: {}\n", suggestion.description));
|
||||
output.push_str(&format!("Confidence: {:.0}%\n", suggestion.confidence * 100.0));
|
||||
output.push_str(&format!("Files Changed: {}\n", suggestion.file_count));
|
||||
output.push_str("\nChanged Files:\n");
|
||||
for file in files { output.push_str(&format!(" [{}] {}\n", file.status_short(), file.path)); }
|
||||
output
|
||||
}
|
||||
}
|
||||
|
||||
pub struct CompactFormatter;
|
||||
impl OutputFormatter for CompactFormatter {
|
||||
fn format(&self, suggestion: &CommitSuggestion, _files: &[ChangedFile]) -> String {
|
||||
match &suggestion.scope {
|
||||
Some(scope) => format!("{}{}{}", suggestion.commit_type, scope, suggestion.description),
|
||||
None => format!("{}{}", suggestion.commit_type, suggestion.description),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct JsonFormatter;
|
||||
impl OutputFormatter for JsonFormatter {
|
||||
fn format(&self, suggestion: &CommitSuggestion, files: &[ChangedFile]) -> String {
|
||||
let json_output = JsonOutput {
|
||||
prefix: match &suggestion.scope {
|
||||
Some(scope) => format!("{}({}): {}", suggestion.commit_type, scope, suggestion.description),
|
||||
None => format!("{}: {}", suggestion.commit_type, suggestion.description),
|
||||
},
|
||||
commit_type: suggestion.commit_type.to_string(),
|
||||
scope: suggestion.scope.as_ref().map(|s| s.name.clone()),
|
||||
description: suggestion.description.clone(), confidence: suggestion.confidence,
|
||||
file_count: suggestion.file_count,
|
||||
files: files.iter().map(|f| JsonFileChange { path: f.path.clone(), status: f.status_short().to_string(), additions: f.additions, deletions: f.deletions }).collect(),
|
||||
};
|
||||
serde_json::to_string_pretty(&json_output).unwrap_or_else(|_| String::from("{}"))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_formatter(format: &OutputFormat) -> Box<dyn OutputFormatter> {
|
||||
match format {
|
||||
OutputFormat::Short => Box::new(ShortFormatter),
|
||||
OutputFormat::Verbose => Box::new(VerboseFormatter),
|
||||
OutputFormat::Compact => Box::new(CompactFormatter),
|
||||
OutputFormat::Json => Box::new(JsonFormatter),
|
||||
}
|
||||
}
|
||||
|
||||
impl ChangedFile {
|
||||
pub fn status_short(&self) -> char {
|
||||
match self.status {
|
||||
super::analyzer::ChangeStatus::Added => 'A', super::analyzer::ChangeStatus::Deleted => 'D',
|
||||
super::analyzer::ChangeStatus::Modified => 'M', super::analyzer::ChangeStatus::Renamed => 'R',
|
||||
super::analyzer::ChangeStatus::Copied => 'C', super::analyzer::ChangeStatus::TypeChanged => 'T',
|
||||
super::analyzer::ChangeStatus::Untracked => '?',
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)] mod tests {
|
||||
use super::*; use crate::convention::{Scope, CommitSuggestion}; use crate::analyzer::ChangeStatus;
|
||||
#[test] fn test_short_formatter() {
|
||||
let suggestion = CommitSuggestion { commit_type: crate::convention::CommitType::Feat, scope: Some(Scope::new("auth".to_string())), description: "add login".to_string(), confidence: 0.9, file_count: 2 };
|
||||
assert_eq!(ShortFormatter.format(&suggestion, &[]), "feat(auth): add login");
|
||||
}
|
||||
#[test] fn test_short_formatter_no_scope() {
|
||||
let suggestion = CommitSuggestion { commit_type: crate::convention::CommitType::Fix, scope: None, description: "fix bug".to_string(), confidence: 0.9, file_count: 1 };
|
||||
assert_eq!(ShortFormatter.format(&suggestion, &[]), "fix: fix bug");
|
||||
}
|
||||
#[test] fn test_get_formatter() {
|
||||
assert!(get_formatter(&OutputFormat::Short).is::<ShortFormatter>());
|
||||
assert!(get_formatter(&OutputFormat::Json).is::<JsonFormatter>());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user