Skip to content

Commit

Permalink
feat(commit): reimplement custom commits
Browse files Browse the repository at this point in the history
  • Loading branch information
oknozor committed Sep 12, 2020
1 parent fe0e143 commit 9f29665
Show file tree
Hide file tree
Showing 7 changed files with 253 additions and 272 deletions.
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ serde = "^1"
tempdir = "^0"
semver = "0.10.0"
moins = "0.4.0"
lazy_static = "1.4.0"
clap = { version = "^2", optional = true }

[features]
Expand Down
16 changes: 8 additions & 8 deletions src/bin/cog.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
use anyhow::Result;
use clap::{App, AppSettings, Arg, SubCommand};
use cocogitto::commit::CommitType;
use cocogitto::{CocoGitto, CommitFilter, CommitFilters, VersionIncrement};
use cocogitto::filter::{CommitFilter, CommitFilters};
use cocogitto::{CocoGitto, VersionIncrement};
use moins::Moins;
use std::process::exit;

Expand All @@ -25,16 +26,15 @@ const VERIFY: &str = "verify";
const CHANGELOG: &str = "changelog";

fn main() -> Result<()> {
let cocogitto = CocoGitto::get().unwrap_or_else(|err| panic!("{}", err));
let cocogitto = CocoGitto::get()?;

let commit_subcommands = cocogitto
.allowed_commits
let commit_subcommands = CocoGitto::get_commit_metadata()
.iter()
.map(|commit_type| {
SubCommand::with_name(commit_type.0)
.map(|(commit_type, commit_config)| {
SubCommand::with_name(commit_type.get_key_str())
.settings(SUBCOMMAND_SETTINGS)
.about(&*commit_type.1.help_message)
.help("Create a pre-formatted commit")
.about(commit_config.help_message.as_str())
.help(commit_config.help_message.as_str())
.arg(Arg::with_name("message").help("The commit message"))
.arg(Arg::with_name("scope").help("The scope of the commit message"))
.arg(Arg::with_name("body").help("The body of the commit message"))
Expand Down
28 changes: 9 additions & 19 deletions src/changelog.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use crate::commit::{Commit, CommitType};
use crate::COMMITS_METADATA;
use colored::*;
use git2::Oid;

Expand All @@ -21,36 +22,25 @@ impl Changelog {
short_from, short_to, self.date
));

let mut add_commit_section = |commit_type: CommitType| {
let add_commit_section = |commit_type: &CommitType| {
let commits: Vec<Commit> = self
.commits
.drain_filter(|commit| commit.message.commit_type == commit_type)
.drain_filter(|commit| &commit.message.commit_type == commit_type)
.collect();

let metadata = COMMITS_METADATA.get(&commit_type).unwrap();
if !commits.is_empty() {
out.push_str(&format!(
"\n### {}\n\n",
commit_type.get_markdown_title().red()
));
out.push_str(&format!("\n### {}\n\n", metadata.changelog_title.red()));
commits
.iter()
.for_each(|commit| out.push_str(&commit.to_markdown()));
}
};

add_commit_section(CommitType::Feature);
add_commit_section(CommitType::BugFix);
add_commit_section(CommitType::Performances);
add_commit_section(CommitType::Revert);
add_commit_section(CommitType::Chore);
add_commit_section(CommitType::Documentation);
add_commit_section(CommitType::Style);
add_commit_section(CommitType::Refactoring);
add_commit_section(CommitType::Test);
add_commit_section(CommitType::Build);
add_commit_section(CommitType::Ci);

// TODO: add commit type from config
COMMITS_METADATA
.iter()
.map(|(commit_type, _)| commit_type)
.for_each(add_commit_section);

out
}
Expand Down
115 changes: 58 additions & 57 deletions src/commit.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use crate::commit::CommitType::*;
use crate::error::CocoGittoError::CommitFormatError;
use crate::COMMITS_METADATA;
use anyhow::Result;
use chrono::{NaiveDateTime, Utc};
use colored::*;
Expand All @@ -16,6 +17,22 @@ pub struct Commit {
pub(crate) date: NaiveDateTime,
}

#[derive(Hash, Eq, PartialEq, Debug, Clone)]
pub enum CommitType {
Feature,
BugFix,
Chore,
Revert,
Performances,
Documentation,
Style,
Refactoring,
Test,
Build,
Ci,
Custom(String),
}

#[derive(Debug, Eq, PartialEq)]
pub struct CommitMessage {
pub(crate) commit_type: CommitType,
Expand All @@ -26,6 +43,12 @@ pub struct CommitMessage {
pub(crate) is_breaking_change: bool,
}

#[derive(Debug, Deserialize, Clone)]
pub struct CommitConfig {
pub changelog_title: String,
pub help_message: String,
}

#[derive(Debug, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum SortCommit {
Expand All @@ -35,6 +58,15 @@ pub enum SortCommit {
ByTypeAndScope,
}

impl CommitConfig {
pub(crate) fn new(changelog_title: &str, help_message: &str) -> Self {
CommitConfig {
changelog_title: changelog_title.to_string(),
help_message: help_message.to_string(),
}
}
}

impl Commit {
pub fn from_git_commit(commit: &Git2Commit) -> Result<Self> {
let shorthand = commit
Expand Down Expand Up @@ -98,10 +130,10 @@ impl Commit {
type_and_scope = &type_and_scope[0..type_and_scope.len() - 1];
}

let commit_type;
let commit_type_str;

let scope: Option<String> = if let Some(left_par_idx) = type_and_scope.find('(') {
commit_type = CommitType::from(&type_and_scope[0..left_par_idx]);
commit_type_str = &type_and_scope[0..left_par_idx];

Some(
type_and_scope
Expand All @@ -112,7 +144,7 @@ impl Commit {
})?,
)
} else {
commit_type = CommitType::from(type_and_scope);
commit_type_str = type_and_scope;
None
};

Expand All @@ -137,8 +169,11 @@ impl Commit {
|| footer.contains("BREAKING-CHANGE")
}

if let CommitType::Unknown(type_str) = commit_type {
return Err(anyhow!("unknown commit type `{}`", type_str.red()));
let commit_type = CommitType::from(commit_type_str);
let allowed_commit = COMMITS_METADATA.get(&commit_type);

if allowed_commit.is_none() {
return Err(anyhow!("unknown commit type `{}`", commit_type_str.red()));
};

Ok(CommitMessage {
Expand Down Expand Up @@ -238,57 +273,21 @@ impl fmt::Display for Commit {
}
}

#[derive(Eq, PartialEq, Debug, Clone)]
pub enum CommitType {
Feature,
BugFix,
Chore,
Revert,
Performances,
Documentation,
Style,
Refactoring,
Test,
Build,
Ci,
Unknown(String),
Custom(String, String),
}

impl CommitType {
pub(crate) fn get_markdown_title(&self) -> &str {
match self {
Feature => "Feature",
BugFix => "Bug Fixes",
Chore => "Miscellaneous Chores",
Revert => "Revert",
Performances => "Performance Improvements",
Documentation => "Documentation",
Style => "Style",
Refactoring => "Refactoring",
Test => "Tests",
Build => "Build System",
Ci => "Continuous Integration",
Custom(_, value) => value,
Unknown(_) => unreachable!(),
}
}

pub(crate) fn get_key_str(&self) -> String {
pub fn get_key_str(&self) -> &str {
match &self {
Feature => "feat".to_string(),
BugFix => "fix".to_string(),
Chore => "chore".to_string(),
Revert => "revert".to_string(),
Performances => "perf".to_string(),
Documentation => "docs".to_string(),
Style => "style".to_string(),
Refactoring => "refactor".to_string(),
Test => "test".to_string(),
Build => "build".to_string(),
Ci => "ci".to_string(),
Custom(key, _) => key.to_owned(),
Unknown(_) => unreachable!(),
Feature => "feat",
BugFix => "fix",
Chore => "chore",
Revert => "revert",
Performances => "perf",
Documentation => "docs",
Style => "style",
Refactoring => "refactor",
Test => "test",
Build => "build",
Ci => "ci",
Custom(key) => key,
}
}
}
Expand Down Expand Up @@ -333,7 +332,7 @@ impl From<&str> for CommitType {
"test" => Test,
"build" => Build,
"ci" => Ci,
other => Unknown(other.to_string()),
other => Custom(other.to_string()),
}
}
}
Expand All @@ -359,17 +358,19 @@ impl Ord for Commit {
#[cfg(test)]
mod test {
use super::Commit;
use crate::commit::CommitType;

#[test]
fn should_map_conventional_commit_message_to_struct() {
// Arrange
let message = "feat(database): add postgresql driver";

// Act
let commit = Commit::from_raw_message(message);
let commit = Commit::parse_commit_message(message);

// Assert
assert_eq!(commit.commit_type, "feat".to_owned());
let commit = commit.unwrap();
assert_eq!(commit.commit_type, CommitType::Feature);
assert_eq!(commit.scope, Some("database".to_owned()));
assert_eq!(commit.description, "add postgresql driver".to_owned());
}
Expand Down
88 changes: 88 additions & 0 deletions src/filter.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
use crate::commit::Commit;
use crate::commit::CommitType;
use git2::Commit as Git2Commit;

#[derive(Eq, PartialEq)]
pub enum CommitFilter {
Type(CommitType),
Scope(String),
Author(String),
BreakingChange,
NoError,
}

pub struct CommitFilters(pub Vec<CommitFilter>);

impl CommitFilters {
pub fn no_error(&self) -> bool {
!self.0.contains(&CommitFilter::NoError)
}

pub fn filter_git2_commit(&self, commit: &Git2Commit) -> bool {
// Author filters
let authors = self
.0
.iter()
.filter_map(|filter| match filter {
CommitFilter::Author(author) => Some(author),
_ => None,
})
.collect::<Vec<&String>>();

let filter_authors = if authors.is_empty() {
true
} else {
authors
.iter()
.any(|author| Some(author.as_str()) == commit.author().name())
};

filter_authors
}
pub fn filters(&self, commit: &Commit) -> bool {
// Commit type filters
let types = self
.0
.iter()
.filter_map(|filter| match filter {
CommitFilter::Type(commit_type) => Some(commit_type),
_ => None,
})
.collect::<Vec<&CommitType>>();

let filter_type = if types.is_empty() {
true
} else {
types
.iter()
.any(|commit_type| **commit_type == commit.message.commit_type)
};

// Scope filters
let scopes = self
.0
.iter()
.filter_map(|filter| match filter {
CommitFilter::Scope(scope) => Some(scope),
_ => None,
})
.collect::<Vec<&String>>();

let filter_scopes = if scopes.is_empty() {
true
} else {
scopes
.iter()
.any(|scope| Some(*scope) == commit.message.scope.as_ref())
};

// Breaking changes filters
let filter_breaking_changes = if self.0.contains(&CommitFilter::BreakingChange) {
commit.message.is_breaking_change
} else {
true
};

filter_type && filter_scopes && filter_breaking_changes
}
}
Loading

0 comments on commit 9f29665

Please sign in to comment.