diff --git a/Cargo.lock b/Cargo.lock index b8f89a0..9f93df9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -180,6 +180,7 @@ dependencies = [ "inquire", "serde", "serde_json", + "serial_test", ] [[package]] @@ -207,6 +208,19 @@ dependencies = [ "winapi", ] +[[package]] +name = "dashmap" +version = "5.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "978747c1d849a7d2ee5e8adc0159961c48fb7e5db2f06af6723b80123bb53856" +dependencies = [ + "cfg-if", + "hashbrown", + "lock_api", + "once_cell", + "parking_lot_core", +] + [[package]] name = "difflib" version = "0.4.0" @@ -285,6 +299,83 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +[[package]] +name = "futures" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23342abe12aba583913b2e62f22225ff9c950774065e4bfb61a19cd9770fec40" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "955518d47e09b25bbebc7a18df10b81f0c766eaf4c4f1cccef2fca5f2a4fb5f2" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c" + +[[package]] +name = "futures-executor" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccecee823288125bd88b4d7f565c9e58e41858e47ab72e8ea2d64e93624386e0" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fff74096e71ed47f8e023204cfd0aa1289cd54ae5430a9523be060cdb849964" + +[[package]] +name = "futures-sink" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f43be4fe21a13b9781a69afa4985b0f6ee0e1afab2c6f454a8cf30e2b2237b6e" + +[[package]] +name = "futures-task" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76d3d132be6c0e6aa1534069c705a74a5997a356c0dc2f86a47765e5617c5b65" + +[[package]] +name = "futures-util" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + [[package]] name = "getrandom" version = "0.2.10" @@ -320,6 +411,12 @@ dependencies = [ "walkdir", ] +[[package]] +name = "hashbrown" +version = "0.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7dfda62a12f55daeae5015f81b0baea145391cb4520f86c248fc615d72640d12" + [[package]] name = "heck" version = "0.4.1" @@ -470,6 +567,18 @@ dependencies = [ "windows-targets", ] +[[package]] +name = "pin-project-lite" +version = "0.2.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + [[package]] name = "predicates" version = "3.0.3" @@ -639,6 +748,31 @@ dependencies = [ "serde", ] +[[package]] +name = "serial_test" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e56dd856803e253c8f298af3f4d7eb0ae5e23a737252cd90bb4f3b435033b2d" +dependencies = [ + "dashmap", + "futures", + "lazy_static", + "log", + "parking_lot", + "serial_test_derive", +] + +[[package]] +name = "serial_test_derive" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91d129178576168c589c9ec973feedf7d3126c01ac2bf08795109aa35b69fb8f" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "signal-hook" version = "0.3.17" @@ -669,6 +803,15 @@ dependencies = [ "libc", ] +[[package]] +name = "slab" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" +dependencies = [ + "autocfg", +] + [[package]] name = "smallvec" version = "1.11.0" diff --git a/Cargo.toml b/Cargo.toml index 5e62442..e65bd73 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,6 +23,7 @@ anyhow = "1.0.75" [dev-dependencies] assert_fs = "1.0.13" +serial_test = "2.0.0" [package.metadata.deb] name = "commit" diff --git a/commit-default.json b/commit-default.json deleted file mode 100644 index c1fcb1e..0000000 --- a/commit-default.json +++ /dev/null @@ -1,43 +0,0 @@ -{ - "config": { - "subject_separator": ": ", - "scope_prefix": "(", - "scope_suffix": ")" - }, - "commit_types": [ - { "name": "feat", "description": "A new feature" }, - { "name": "fix", "description": "A bug fix" }, - { "name": "docs", "description": "Documentation only changes" }, - { - "name": "style", - "description": "Changes that do not affect the meaning of the code (white-space, formatting, missing semi-colons, etc)" - }, - { - "name": "refactor", - "description": "A code change that neither fixes a bug nor adds a feature" - }, - { - "name": "perf", - "description": "A code change that improves performance" - }, - { - "name": "test", - "description": "Adding missing tests or correcting existing tests" - }, - { - "name": "chore", - "description": "Other changes that don't modify src or test files" - } - ], - "commit_scopes": [ - { "name": "custom", "description": "Custom scope" }, - { "name": "none", "description": "No scope" } - ], - "msg": { - "commit_type": "What type of commit you will made?", - "commit_scope": "What scope of commit you will made? (Optional)", - "commit_description": "Write a SHORT, IMPERATIVE tense description of the change:", - "commit_body": "Provide a LONGER description of the change (Optional):", - "commit_footer": "List any ISSUES CLOSED by this change E.g.: #31, #34 (Optional):" - } -} diff --git a/commit.json b/commit.json index ddb59ca..d2ad227 100644 --- a/commit.json +++ b/commit.json @@ -1,44 +1,5 @@ { "config": { - "subject_separator": ": ", - "scope_prefix": "(", - "scope_suffix": ")", "pre_commit": "./pre-commit.sh" - }, - "commit_types": [ - { "name": "feat", "description": "A new feature" }, - { "name": "fix", "description": "A bug fix" }, - { "name": "docs", "description": "Documentation only changes" }, - { - "name": "style", - "description": "Changes that do not affect the meaning of the code (white-space, formatting, missing semi-colons, etc)" - }, - { - "name": "refactor", - "description": "A code change that neither fixes a bug nor adds a feature" - }, - { - "name": "perf", - "description": "A code change that improves performance" - }, - { - "name": "test", - "description": "Adding missing tests or correcting existing tests" - }, - { - "name": "chore", - "description": "Other changes that don't modify src or test files" - } - ], - "commit_scopes": [ - { "name": "custom", "description": "Custom scope" }, - { "name": "none", "description": "No scope" } - ], - "msg": { - "commit_type": "What type of commit you will made?", - "commit_scope": "What scope of commit you will made? (Optional)", - "commit_description": "Write a SHORT, IMPERATIVE tense description of the change:", - "commit_body": "Provide a LONGER description of the change (Optional):", - "commit_footer": "List any ISSUES CLOSED by this change E.g.: #31, #34 (Optional):" } } diff --git a/src/commit_message/message_build/mod.rs b/src/commit_message/message_build/mod.rs index ef4b8e4..e544e7d 100644 --- a/src/commit_message/message_build/mod.rs +++ b/src/commit_message/message_build/mod.rs @@ -1,7 +1,7 @@ #[cfg(test)] mod tests; -use crate::config::Config; +use crate::commit_pattern::Config; pub struct MessageBuilder { config: Config, diff --git a/src/commit_message/message_build/tests.rs b/src/commit_message/message_build/tests.rs index df731b9..18fdd97 100644 --- a/src/commit_message/message_build/tests.rs +++ b/src/commit_message/message_build/tests.rs @@ -1,5 +1,5 @@ use crate::commit_message::message_build::MessageBuilder; -use crate::config::Config; +use crate::commit_pattern::Config; fn message_with_config(config: Config) -> String { let mut builder = MessageBuilder::new(config); diff --git a/src/commit_message/mod.rs b/src/commit_message/mod.rs index 535ad84..fc082b5 100644 --- a/src/commit_message/mod.rs +++ b/src/commit_message/mod.rs @@ -2,11 +2,11 @@ mod message_build; mod prompt; use anyhow::{anyhow, Result}; - -use crate::config::CommitPattern; use message_build::MessageBuilder; use prompt::Prompt; +use crate::commit_pattern::CommitPattern; + pub fn make_message_commit(pattern: CommitPattern) -> Result { let mut message_inquirer = MessageInquirer::new(pattern.clone()); let skip_commit = pattern.skip_commit; diff --git a/src/commit_message/prompt.rs b/src/commit_message/prompt.rs index 72a2b60..3607ee9 100644 --- a/src/commit_message/prompt.rs +++ b/src/commit_message/prompt.rs @@ -1,4 +1,4 @@ -use crate::config::Type; +use crate::commit_pattern::Type; use inquire::{ required, diff --git a/src/commit_pattern/config.rs b/src/commit_pattern/config.rs new file mode 100644 index 0000000..6ec2fa0 --- /dev/null +++ b/src/commit_pattern/config.rs @@ -0,0 +1,39 @@ +use serde::Deserialize; + +#[derive(Deserialize, Clone)] +pub struct Config { + pub type_prefix: Option, + pub type_suffix: Option, + #[serde(default = "Config::subject_separator")] + pub subject_separator: String, + #[serde(default = "Config::scope_prefix")] + pub scope_prefix: String, + #[serde(default = "Config::scope_suffix")] + pub scope_suffix: String, + pub pre_commit: Option, +} + +impl Config { + fn subject_separator() -> String { + ": ".to_owned() + } + fn scope_prefix() -> String { + "(".to_owned() + } + fn scope_suffix() -> String { + ")".to_owned() + } +} + +impl Default for Config { + fn default() -> Self { + Self { + type_prefix: None, + type_suffix: None, + subject_separator: Self::subject_separator(), + scope_prefix: Self::scope_prefix(), + scope_suffix: Self::scope_suffix(), + pre_commit: None, + } + } +} diff --git a/src/commit_pattern/messages.rs b/src/commit_pattern/messages.rs new file mode 100644 index 0000000..970572d --- /dev/null +++ b/src/commit_pattern/messages.rs @@ -0,0 +1,46 @@ +use serde::Deserialize; + +#[derive(Deserialize, Clone)] +pub struct Messages { + #[serde(default = "Messages::commit_type")] + pub commit_type: String, + #[serde(default = "Messages::commit_scope")] + pub commit_scope: String, + #[serde(default = "Messages::commit_description")] + pub commit_description: String, + #[serde(default = "Messages::commit_body")] + pub commit_body: String, + #[serde(default = "Messages::commit_footer")] + pub commit_footer: String, +} + +impl Messages { + fn commit_type() -> String { + "What type of commit you will made?".to_owned() + } + fn commit_scope() -> String { + "What scope of commit you will made? (Optional)".to_owned() + } + fn commit_description() -> String { + "Write a SHORT, IMPERATIVE tense description of the change:".to_owned() + } + fn commit_body() -> String { + "Provide a LONGER description of the change (Optional):".to_owned() + } + + fn commit_footer() -> String { + "List any ISSUES CLOSED by this change E.g.: #31, #34 (Optional):".to_owned() + } +} + +impl Default for Messages { + fn default() -> Self { + Self { + commit_type: Self::commit_type(), + commit_scope: Self::commit_scope(), + commit_description: Self::commit_description(), + commit_body: Self::commit_body(), + commit_footer: Self::commit_footer(), + } + } +} diff --git a/src/commit_pattern/mod.rs b/src/commit_pattern/mod.rs new file mode 100644 index 0000000..230dd78 --- /dev/null +++ b/src/commit_pattern/mod.rs @@ -0,0 +1,85 @@ +mod config; +mod messages; + +use serde::Deserialize; +use std::fmt::{Display, Formatter, Result as FmtResult}; + +pub use config::Config; +use messages::Messages; + +impl Display for Type { + fn fmt(&self, formatter: &mut Formatter) -> FmtResult { + write!(formatter, "{} - {}", self.name, self.description) + } +} + +#[derive(Clone, Deserialize)] +pub struct Type { + pub name: String, + pub description: String, +} + +#[derive(Deserialize, Clone)] +pub struct CommitPattern { + #[serde(default)] + pub config: Config, + #[serde(default = "CommitPattern::commit_types")] + pub commit_types: Vec, + #[serde(default = "CommitPattern::commit_scopes")] + pub commit_scopes: Vec, + #[serde(default)] + pub skip_commit: Vec, + #[serde(default)] + pub msg: Messages, +} + +impl CommitPattern { + fn commit_types() -> Vec { + vec![ + Type { + name: "feat".to_owned(), + description: "A new feature".to_owned(), + }, + Type { + name: "fix".to_owned(), + description: "A bug fix".to_owned(), + }, + Type { + name: "docs".to_owned(), + description: "Documentation only changes".to_owned(), + }, + Type { + name: "style".to_owned(), + description: "Changes that do not affect the meaning of the code (white-space, formatting, missing semi-colons, etc)".to_owned(), + }, + Type { + name: "refactor".to_owned(), + description: "A code change that neither fixes a bug nor adds a feature".to_owned(), + }, + Type { + name: "perf".to_owned(), + description: "A code change that improves performance".to_owned(), + }, + Type { + name: "test".to_owned(), + description: "Adding missing tests or correcting existing tests".to_owned(), + }, + Type { + name: "chore".to_owned(), + description: "Other changes that don't modify src or test files".to_owned(), + }, + ] + } + fn commit_scopes() -> Vec { + vec![ + Type { + name: "custom".to_owned(), + description: "Custom scope".to_owned(), + }, + Type { + name: "none".to_owned(), + description: "No scope".to_owned(), + }, + ] + } +} diff --git a/src/config/mod.rs b/src/config/mod.rs index 07ae18d..552f462 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -2,54 +2,11 @@ mod tests; use anyhow::{anyhow, Context, Result}; -use serde::Deserialize; -use std::fmt::{Display, Formatter, Result as FmtResult}; use std::fs; use std::path::Path; use std::path::PathBuf; -use crate::DEFAULT_CONFIG_FILE; - -#[derive(Deserialize, Clone)] -pub struct Config { - pub type_prefix: Option, - pub type_suffix: Option, - pub subject_separator: String, - pub scope_prefix: String, - pub scope_suffix: String, - pub pre_commit: Option, -} - -impl Display for Type { - fn fmt(&self, formatter: &mut Formatter) -> FmtResult { - write!(formatter, "{} - {}", self.name, self.description) - } -} - -#[derive(Clone, Deserialize)] -pub struct Type { - pub name: String, - pub description: String, -} - -#[derive(Deserialize, Clone)] -pub struct Messages { - pub commit_type: String, - pub commit_scope: String, - pub commit_description: String, - pub commit_body: String, - pub commit_footer: String, -} - -#[derive(Deserialize, Clone)] -pub struct CommitPattern { - pub config: Config, - pub commit_types: Vec, - pub commit_scopes: Vec, - #[serde(default)] - pub skip_commit: Vec, - pub msg: Messages, -} +use crate::commit_pattern::CommitPattern; fn get_config_path_content(config_path: impl AsRef) -> Result { let content = fs::read_to_string(config_path)?; @@ -92,9 +49,8 @@ fn get_config_path() -> Result { } pub fn get_pattern(config_path: Option) -> Result { - let default_pattern_str = DEFAULT_CONFIG_FILE; let selected_config_path = select_custom_config_path(config_path)?; - let pattern_str = get_config_path_content(selected_config_path) - .unwrap_or_else(|_| default_pattern_str.to_owned()); + let pattern_str = + get_config_path_content(selected_config_path).unwrap_or_else(|_| "{}".to_owned()); serde_json::from_str(&pattern_str).context("Failed to parse commit pattern from file") } diff --git a/src/config/tests.rs b/src/config/tests.rs index 1787a10..fef8fb4 100644 --- a/src/config/tests.rs +++ b/src/config/tests.rs @@ -2,8 +2,10 @@ use std::env::set_current_dir; use super::*; use assert_fs::prelude::*; +use serial_test::serial; #[test] +#[serial] fn select_custom_config_path_test() -> Result<()> { let temp_dir = assert_fs::TempDir::new()?; let config_file = temp_dir.child("config.json"); @@ -27,6 +29,7 @@ fn select_custom_config_path_test() -> Result<()> { } #[test] +#[serial] fn get_config_path_test() -> Result<()> { let temp_dir = assert_fs::TempDir::new()?; set_current_dir(temp_dir.path())?; @@ -39,6 +42,7 @@ fn get_config_path_test() -> Result<()> { } #[test] +#[serial] fn get_config_path_content_test() -> Result<()> { let temp_dir = assert_fs::TempDir::new()?; let config_file = temp_dir.child("config.json"); @@ -47,7 +51,7 @@ fn get_config_path_content_test() -> Result<()> { let content = get_config_path_content(config_path)?; assert_eq!(content, ""); - let expected = include_str!("../../commit-default.json"); + let expected = include_str!("../../commit.json"); config_file.write_str(expected)?; let content = get_config_path_content(config_path)?; assert_eq!(content, expected); @@ -55,6 +59,7 @@ fn get_config_path_content_test() -> Result<()> { } #[test] +#[serial] fn get_pattern_test() -> Result<()> { let temp_dir = assert_fs::TempDir::new()?; set_current_dir(temp_dir.path())?; diff --git a/src/main.rs b/src/main.rs index 9134db8..73501ef 100644 --- a/src/main.rs +++ b/src/main.rs @@ -13,11 +13,11 @@ mod commit; mod commit_message; +mod commit_pattern; mod config; use anyhow::Result; use clap::Parser; -use std::io::Write; use std::path::PathBuf; use commit::{ @@ -26,17 +26,12 @@ use commit::{ }; use commit_message::make_message_commit; -const DEFAULT_CONFIG_FILE: &str = include_str!("../commit-default.json"); - #[derive(Parser, Debug)] #[command(about, author, version)] struct Args { /// Custom configuration file path #[arg(short, long)] config: Option, - /// Init custom configuration file - #[arg(long)] - init: bool, /// Use as hook #[arg(long)] hook: bool, @@ -57,12 +52,6 @@ fn main() -> Result<()> { check_staged_files()?; - if args.init { - let mut file = std::fs::File::create("commit.json")?; - file.write_all(DEFAULT_CONFIG_FILE.as_bytes())?; - return Ok(()); - } - let pattern = config::get_pattern(args.config)?; if args.retry {