init
This commit is contained in:
commit
0a48113d38
10 changed files with 1660 additions and 0 deletions
89
src/args.rs
Normal file
89
src/args.rs
Normal file
|
@ -0,0 +1,89 @@
|
|||
use clap::{arg, command};
|
||||
|
||||
pub fn get_args() -> clap::ArgMatches {
|
||||
command!()
|
||||
.about("Git wrapper")
|
||||
.subcommand(
|
||||
command!("bootstrap")
|
||||
.about("Bootstrap a new repository from base repository")
|
||||
.arg(arg!(<BASE> "Base repository").required(true))
|
||||
.arg(arg!(<NEW> "Repository name").required(true))
|
||||
.alias("bs"),
|
||||
)
|
||||
.subcommand(
|
||||
command!("todo")
|
||||
.about("Show open TODOs in repository")
|
||||
.alias("t"),
|
||||
)
|
||||
.subcommand(
|
||||
command!("new")
|
||||
.about("Create a new branch")
|
||||
.arg(arg!(<BRANCH> "Branch name").required(true))
|
||||
.alias("n"),
|
||||
)
|
||||
.subcommand(
|
||||
command!("commit")
|
||||
.about("Commit current changes")
|
||||
.alias("c")
|
||||
.arg(arg!(-a --all "Add all changed files to commit"))
|
||||
.arg(arg!(-d --done "Only allow commiting if no TODOs are present"))
|
||||
.arg(
|
||||
arg!(-i --interactive "Write interactive commit message with gitmoji standard")
|
||||
.conflicts_with("message"),
|
||||
)
|
||||
.arg(arg!(-m --message <MSG> "Commit message").required(false)),
|
||||
)
|
||||
.subcommand(
|
||||
command!("remove")
|
||||
.about("Remove a branch")
|
||||
.arg(arg!(<BRANCH> "Branch name").required(true))
|
||||
.alias("r"),
|
||||
)
|
||||
.subcommand(command!("branch").about("Show branches").alias("b"))
|
||||
.subcommand(
|
||||
command!()
|
||||
.name("push")
|
||||
.about("Push to a remote")
|
||||
.arg(arg!(-f --force "Force push"))
|
||||
.alias("p"),
|
||||
)
|
||||
.subcommand(
|
||||
command!()
|
||||
.name("status")
|
||||
.about("Show git status")
|
||||
.alias("stat")
|
||||
.alias("stats"),
|
||||
)
|
||||
.subcommand(
|
||||
command!()
|
||||
.name("add")
|
||||
.about("Add files to git")
|
||||
.alias("a")
|
||||
.arg(arg!(<FILE> "File to add").required(true)),
|
||||
)
|
||||
.subcommand(
|
||||
command!()
|
||||
.name("fetch")
|
||||
.about("Run git fetch --prune")
|
||||
.alias("f"),
|
||||
)
|
||||
.subcommand(
|
||||
command!()
|
||||
.name("pull")
|
||||
.about("Run git pull --prune")
|
||||
.alias("p"),
|
||||
)
|
||||
.subcommand(
|
||||
command!()
|
||||
.name("log")
|
||||
.about("Show git commit log")
|
||||
.alias("l"),
|
||||
)
|
||||
.subcommand(
|
||||
command!()
|
||||
.name("save")
|
||||
.about("Create a WIP commit")
|
||||
.alias("s"),
|
||||
)
|
||||
.get_matches()
|
||||
}
|
260
src/git.rs
Normal file
260
src/git.rs
Normal file
|
@ -0,0 +1,260 @@
|
|||
use subprocess::Exec;
|
||||
use yansi::{Color, Paint};
|
||||
|
||||
use crate::{
|
||||
NO_COMMIT_REGEX, no_commit_amount, precommit::rust_pre_commit, read_stdout, show_rg,
|
||||
todos_amount,
|
||||
};
|
||||
|
||||
/// Check if the current repository has a remote set up
|
||||
pub fn has_remote() -> bool {
|
||||
let e = Exec::cmd("git").arg("remote").arg("-v");
|
||||
let str = read_stdout(e);
|
||||
!str.is_empty()
|
||||
}
|
||||
|
||||
/// Create a new branch and switch to it in the current repository
|
||||
pub fn create_new_branch(branch: &str) {
|
||||
let mut git = Exec::cmd("git")
|
||||
.arg("checkout")
|
||||
.arg("-b")
|
||||
.arg(branch)
|
||||
.popen()
|
||||
.unwrap();
|
||||
git.wait().unwrap();
|
||||
if has_remote() {
|
||||
let mut git = Exec::cmd("git")
|
||||
.arg("push")
|
||||
.arg("-u")
|
||||
.arg("origin")
|
||||
.arg(branch)
|
||||
.popen()
|
||||
.unwrap();
|
||||
git.wait().unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
/// Push the current repository to remote
|
||||
pub fn push(force: bool) {
|
||||
let mut cmd = Exec::cmd("git")
|
||||
.arg("push")
|
||||
.arg("--set-upstream")
|
||||
.arg("origin");
|
||||
|
||||
if force {
|
||||
cmd = cmd.arg("--force-with-lease");
|
||||
}
|
||||
|
||||
cmd.popen().unwrap().wait().unwrap();
|
||||
}
|
||||
|
||||
/// Delete a branch from the current repository
|
||||
pub fn delete_branch(branch: &str) {
|
||||
let mut git = Exec::cmd("git")
|
||||
.arg("branch")
|
||||
.arg("-d")
|
||||
.arg(branch)
|
||||
.popen()
|
||||
.unwrap();
|
||||
git.wait().unwrap();
|
||||
if has_remote() {
|
||||
let mut git = Exec::cmd("git")
|
||||
.arg("push")
|
||||
.arg("origin")
|
||||
.arg("--delete")
|
||||
.arg(branch)
|
||||
.popen()
|
||||
.unwrap();
|
||||
git.wait().unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the title of the last commit
|
||||
pub fn last_commit() -> String {
|
||||
let e = Exec::cmd("git").arg("log").arg("-1").arg("--pretty=%B");
|
||||
read_stdout(e)
|
||||
}
|
||||
|
||||
/// Get all files affected by last commit
|
||||
pub fn last_commit_files() -> Vec<String> {
|
||||
let file_str = read_stdout(
|
||||
Exec::cmd("git")
|
||||
.arg("diff-tree")
|
||||
.arg("--no-commit-id")
|
||||
.arg("--name-only")
|
||||
.arg("-r")
|
||||
.arg("HEAD"),
|
||||
);
|
||||
|
||||
file_str.split('\n').map(|x| x.to_string()).collect()
|
||||
}
|
||||
|
||||
/// Add a file to git
|
||||
pub fn git_add(file: &str) {
|
||||
Exec::cmd("git")
|
||||
.arg("add")
|
||||
.arg(file)
|
||||
.popen()
|
||||
.unwrap()
|
||||
.wait()
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
/// Get a Vec of programming languages used in this repository
|
||||
pub fn get_languages() -> Vec<String> {
|
||||
let e: Exec = Exec::cmd("onefetch").arg("-o").arg("json");
|
||||
let json_str = read_stdout(e);
|
||||
if let Result::<serde_json::Value, _>::Ok(json) = serde_json::from_str(&json_str) {
|
||||
let languages = (|| {
|
||||
let fields = json.as_object()?.get("infoFields")?.as_array()?;
|
||||
let langs = fields
|
||||
.iter()
|
||||
.find(|x| x.as_object().unwrap().contains_key("LanguagesInfo"))?;
|
||||
let langs = langs
|
||||
.as_object()?
|
||||
.get("LanguagesInfo")?
|
||||
.as_object()?
|
||||
.get("languagesWithPercentage")?
|
||||
.as_array()?;
|
||||
Some(
|
||||
langs
|
||||
.iter()
|
||||
.map(|x| {
|
||||
x.as_object()
|
||||
.unwrap()
|
||||
.get("language")
|
||||
.unwrap()
|
||||
.as_str()
|
||||
.unwrap()
|
||||
.to_string()
|
||||
})
|
||||
.collect(),
|
||||
)
|
||||
})();
|
||||
|
||||
languages.unwrap_or_default()
|
||||
} else {
|
||||
Vec::new()
|
||||
}
|
||||
}
|
||||
|
||||
/// Commit changes to the repository.
|
||||
pub fn commit(all: bool, done: bool, msg: &str) {
|
||||
// Work In Progress Save Commit
|
||||
if last_commit().as_str() == "WIP" {
|
||||
// Get files affected by commit
|
||||
let last_commit_files = last_commit_files();
|
||||
|
||||
// Reset last commit
|
||||
Exec::cmd("git")
|
||||
.arg("reset")
|
||||
.arg("HEAD~1")
|
||||
.popen()
|
||||
.unwrap()
|
||||
.wait()
|
||||
.unwrap();
|
||||
|
||||
// Readd files affected by commit
|
||||
for file in last_commit_files {
|
||||
git_add(&file);
|
||||
}
|
||||
}
|
||||
|
||||
// Check TODOs
|
||||
let todos = todos_amount();
|
||||
|
||||
if todos > 0 {
|
||||
println!(
|
||||
"{}: Repository still contains {todos} TODO entries.",
|
||||
"Warning".paint(Color::Yellow)
|
||||
);
|
||||
|
||||
if done {
|
||||
println!(
|
||||
"{}",
|
||||
"💀 Make sure to close them before commiting or we can't rest in peace..."
|
||||
.paint(Color::Red)
|
||||
);
|
||||
std::process::exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
// Impossible commits
|
||||
if no_commit_amount() > 0 {
|
||||
println!("{}", "Unable to commit because of:".paint(Color::Red));
|
||||
show_rg(NO_COMMIT_REGEX);
|
||||
std::process::exit(1);
|
||||
}
|
||||
|
||||
// Laguage specific pre-commit hooks
|
||||
for lang in get_languages() {
|
||||
match lang.as_str() {
|
||||
"Rust" => {
|
||||
rust_pre_commit();
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
if all {
|
||||
Exec::cmd("git")
|
||||
.arg("add")
|
||||
.arg("-A")
|
||||
.popen()
|
||||
.unwrap()
|
||||
.wait()
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
Exec::cmd("git")
|
||||
.arg("commit")
|
||||
.arg("-m")
|
||||
.arg(msg)
|
||||
.popen()
|
||||
.unwrap()
|
||||
.wait()
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
/// Fetch remote state
|
||||
pub fn fetch() {
|
||||
Exec::cmd("git")
|
||||
.arg("fetch")
|
||||
.arg("--prune")
|
||||
.popen()
|
||||
.unwrap()
|
||||
.wait()
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
/// Pull remote changes
|
||||
pub fn pull() {
|
||||
Exec::cmd("git")
|
||||
.arg("pull")
|
||||
.arg("--prune")
|
||||
.popen()
|
||||
.unwrap()
|
||||
.wait()
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
/// Bootstrap a git repository using a base template
|
||||
pub fn bootstrap(base: &str, name: &str) {
|
||||
Exec::cmd("git")
|
||||
.arg("clone")
|
||||
.arg(base)
|
||||
.arg(name)
|
||||
.popen()
|
||||
.unwrap()
|
||||
.wait()
|
||||
.unwrap();
|
||||
std::fs::remove_dir_all(std::path::Path::new(name).join(".git")).unwrap();
|
||||
Exec::cmd("git")
|
||||
.arg("init")
|
||||
.arg("--quiet")
|
||||
.cwd(std::path::Path::new(name))
|
||||
.popen()
|
||||
.unwrap()
|
||||
.wait()
|
||||
.unwrap();
|
||||
}
|
360
src/gitmoji.rs
Normal file
360
src/gitmoji.rs
Normal file
|
@ -0,0 +1,360 @@
|
|||
pub fn select_from_list<F: FnOnce(&str) -> String>(
|
||||
prompt: &str,
|
||||
lst: Vec<&str>,
|
||||
quit: bool,
|
||||
then: F,
|
||||
) -> String {
|
||||
match inquire::Select::new(prompt, lst).prompt() {
|
||||
Ok(val) => then(val),
|
||||
Err(_) => {
|
||||
if quit {
|
||||
eprintln!("Nothing selected");
|
||||
std::process::exit(1);
|
||||
}
|
||||
|
||||
String::new()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn select_gitmoji() -> String {
|
||||
let topics = vec![
|
||||
"Fix",
|
||||
"Refactor",
|
||||
"Improve",
|
||||
"Add/Update",
|
||||
"Remove",
|
||||
"Git",
|
||||
"UI",
|
||||
"Dependency",
|
||||
"Security",
|
||||
"Text",
|
||||
"Test",
|
||||
"Fun",
|
||||
"Misc",
|
||||
];
|
||||
|
||||
let select = select_from_list(
|
||||
"Gitmoji Intention Topic",
|
||||
topics,
|
||||
true,
|
||||
|topic| match topic {
|
||||
"Fix" => select_gitmoji_fix(),
|
||||
"Improve" => select_gitmoji_improvement(),
|
||||
"Remove" => select_gitmoji_remove(),
|
||||
"Dependency" => select_gitmoji_dependency(),
|
||||
"Test" => select_gitmoji_test(),
|
||||
"Git" => select_gitmoji_git(),
|
||||
"Refactor" => select_gitmoji_refactor(),
|
||||
"UI" => select_gitmoji_ui(),
|
||||
"Text" => select_gitmoji_text(),
|
||||
"Fun" => select_gitmoji_fun(),
|
||||
"Security" => select_gitmoji_security(),
|
||||
"Add/Update" => select_gitmoji_add_update(),
|
||||
_ => select_gitmoji_misc(),
|
||||
},
|
||||
);
|
||||
|
||||
if select.is_empty() {
|
||||
return select_gitmoji();
|
||||
}
|
||||
|
||||
select
|
||||
}
|
||||
|
||||
pub fn select_gitmoji_fix() -> String {
|
||||
let options = vec![
|
||||
"🐛 Fix a bug",
|
||||
"🩹 Simple fix for a non-critical issue",
|
||||
"🚑️ Critical hotfix",
|
||||
"✏️ Fix typos",
|
||||
"💚 Fix CI Build",
|
||||
"🔒️ Fix security or privacy issues",
|
||||
"🚨 Fix compiler / linter warnings",
|
||||
"🥅 Catch errors",
|
||||
];
|
||||
|
||||
let res = select_from_list("Gitmoji Intention", options, false, |selected| {
|
||||
selected.split_whitespace().next().unwrap().to_string()
|
||||
});
|
||||
|
||||
if res.is_empty() {
|
||||
return select_gitmoji();
|
||||
}
|
||||
|
||||
res
|
||||
}
|
||||
|
||||
pub fn select_gitmoji_improvement() -> String {
|
||||
let options = vec![
|
||||
"✨ Introduce new features",
|
||||
"🎨 Improve structure / format of the code",
|
||||
"⚡️ Improve performance",
|
||||
"🧑💻 Improve developer experience",
|
||||
"🚸 Improve user experience / usability",
|
||||
"♿️ Improve accessibility",
|
||||
"🔍️ Improve SEO",
|
||||
];
|
||||
|
||||
let res = select_from_list("Gitmoji Intention", options, false, |selected| {
|
||||
selected.split_whitespace().next().unwrap().to_string()
|
||||
});
|
||||
|
||||
if res.is_empty() {
|
||||
return select_gitmoji();
|
||||
}
|
||||
|
||||
res
|
||||
}
|
||||
|
||||
pub fn select_gitmoji_remove() -> String {
|
||||
let options = vec!["🔥 Remove code or files", "⚰️ Remove dead code"];
|
||||
|
||||
let res = select_from_list("Gitmoji Intention", options, false, |selected| {
|
||||
selected.split_whitespace().next().unwrap().to_string()
|
||||
});
|
||||
|
||||
if res.is_empty() {
|
||||
return select_gitmoji();
|
||||
}
|
||||
|
||||
res
|
||||
}
|
||||
|
||||
pub fn select_gitmoji_dependency() -> String {
|
||||
let options = vec![
|
||||
"➖ Remove a dependency",
|
||||
"➕ Add a dependency",
|
||||
"⬇️ Downgrade dependencies",
|
||||
"⬆️ Upgrade dependencies",
|
||||
"📌 Pin dependencies to specific versions",
|
||||
];
|
||||
|
||||
let res = select_from_list("Gitmoji Intention", options, false, |selected| {
|
||||
selected.split_whitespace().next().unwrap().to_string()
|
||||
});
|
||||
|
||||
if res.is_empty() {
|
||||
return select_gitmoji();
|
||||
}
|
||||
|
||||
res
|
||||
}
|
||||
|
||||
pub fn select_gitmoji_test() -> String {
|
||||
let options = vec![
|
||||
"✅ Add, update, or pass tests",
|
||||
"🧪 Add a failing test",
|
||||
"🤡 Mock things",
|
||||
"⚗️ Perform experiments",
|
||||
];
|
||||
|
||||
let res = select_from_list("Gitmoji Intention", options, false, |selected| {
|
||||
selected.split_whitespace().next().unwrap().to_string()
|
||||
});
|
||||
|
||||
if res.is_empty() {
|
||||
return select_gitmoji();
|
||||
}
|
||||
|
||||
res
|
||||
}
|
||||
|
||||
pub fn select_gitmoji_fun() -> String {
|
||||
let options = vec![
|
||||
"🍻 Write code drunkenly",
|
||||
"🍃 Write code high",
|
||||
"❄ Write code on coke",
|
||||
"🍄 Write code on psychedelics",
|
||||
"🥚 Add or update an easter egg",
|
||||
"💩 Write bad code that needs to be improved",
|
||||
];
|
||||
|
||||
let res = select_from_list("Gitmoji Intention", options, false, |selected| {
|
||||
selected.split_whitespace().next().unwrap().to_string()
|
||||
});
|
||||
|
||||
if res.is_empty() {
|
||||
return select_gitmoji();
|
||||
}
|
||||
|
||||
res
|
||||
}
|
||||
|
||||
pub fn select_gitmoji_git() -> String {
|
||||
let options = vec![
|
||||
"⏪️ Revert changes",
|
||||
"🔀 Merge branches",
|
||||
"🔖 Release / Version tags",
|
||||
"🙈 Add or update a .gitignore file",
|
||||
];
|
||||
|
||||
let res = select_from_list("Gitmoji Intention", options, false, |selected| {
|
||||
selected.split_whitespace().next().unwrap().to_string()
|
||||
});
|
||||
|
||||
if res.is_empty() {
|
||||
return select_gitmoji();
|
||||
}
|
||||
|
||||
res
|
||||
}
|
||||
|
||||
pub fn select_gitmoji_refactor() -> String {
|
||||
let options = vec![
|
||||
"♻️ Refactor code",
|
||||
"🗑️ Deprecate code that needs to be cleaned up",
|
||||
"🚚 Move or rename resources (e.g.: files, paths, routes)",
|
||||
"🍱 Add or update assets",
|
||||
"👽️ Update code due to external API changes",
|
||||
"💡 Add or update comments in source code",
|
||||
"🏷️ Add or update types",
|
||||
];
|
||||
|
||||
let res = select_from_list("Gitmoji Intention", options, false, |selected| {
|
||||
selected.split_whitespace().next().unwrap().to_string()
|
||||
});
|
||||
|
||||
if res.is_empty() {
|
||||
return select_gitmoji();
|
||||
}
|
||||
|
||||
res
|
||||
}
|
||||
|
||||
pub fn select_gitmoji_ui() -> String {
|
||||
let options = vec![
|
||||
"💄 Add or update the UI and style files",
|
||||
"📱 Work on responsive design",
|
||||
"💫 Add or update animations and transitions",
|
||||
];
|
||||
|
||||
let res = select_from_list("Gitmoji Intention", options, false, |selected| {
|
||||
selected.split_whitespace().next().unwrap().to_string()
|
||||
});
|
||||
|
||||
if res.is_empty() {
|
||||
return select_gitmoji();
|
||||
}
|
||||
|
||||
res
|
||||
}
|
||||
|
||||
pub fn select_gitmoji_text() -> String {
|
||||
let options = vec![
|
||||
"📝 Add or update documentation",
|
||||
"🌐 Internationalization and localization",
|
||||
"💬 Add or update text and literals",
|
||||
"👥 Add or update contributor(s)",
|
||||
"📄 Add or update license",
|
||||
"🔊 Add or update logs",
|
||||
"🔇 Remove logs",
|
||||
];
|
||||
|
||||
let res = select_from_list("Gitmoji Intention", options, false, |selected| {
|
||||
selected.split_whitespace().next().unwrap().to_string()
|
||||
});
|
||||
|
||||
if res.is_empty() {
|
||||
return select_gitmoji();
|
||||
}
|
||||
|
||||
res
|
||||
}
|
||||
|
||||
pub fn select_gitmoji_security() -> String {
|
||||
let options = vec![
|
||||
"🛂 Work on code related to authorization, roles and permissions",
|
||||
"🔐 Add or update secrets",
|
||||
"🦺 Add or update code related to validation",
|
||||
];
|
||||
|
||||
let res = select_from_list("Gitmoji Intention", options, false, |selected| {
|
||||
selected.split_whitespace().next().unwrap().to_string()
|
||||
});
|
||||
|
||||
if res.is_empty() {
|
||||
return select_gitmoji();
|
||||
}
|
||||
|
||||
res
|
||||
}
|
||||
|
||||
pub fn select_gitmoji_add_update() -> String {
|
||||
let options = vec![
|
||||
"👷 Add or update CI build system",
|
||||
"🔧 Add or update configuration files",
|
||||
"🔨 Add or update development scripts",
|
||||
"📦️ Add or update compiled files or packages",
|
||||
"🚩 Add, update, or remove feature flags",
|
||||
"🩺 Add or update healthcheck",
|
||||
"🧵 Add or update code related to multithreading or concurrency",
|
||||
"🌱 Add or update seed files",
|
||||
"📸 Add or update snapshots",
|
||||
"📈 Add or update analytics or track code",
|
||||
"👔 Add or update business logic",
|
||||
];
|
||||
|
||||
let res = select_from_list("Gitmoji Intention", options, false, |selected| {
|
||||
selected.split_whitespace().next().unwrap().to_string()
|
||||
});
|
||||
|
||||
if res.is_empty() {
|
||||
return select_gitmoji();
|
||||
}
|
||||
|
||||
res
|
||||
}
|
||||
|
||||
pub fn select_gitmoji_misc() -> String {
|
||||
let options = vec![
|
||||
"🎉 Begin a project",
|
||||
"🚀 Deploy stuff",
|
||||
"🚧 Work in progress",
|
||||
"💥 Introduce breaking changes",
|
||||
"🗃️ Perform database related changes",
|
||||
"🏗️ Make architectural changes",
|
||||
"🧱 Infrastructure related changes",
|
||||
"💸 Add sponsorships or money related infrastructure",
|
||||
"🧐 Data exploration/inspection",
|
||||
];
|
||||
|
||||
let res = select_from_list("Gitmoji Intention", options, false, |selected| {
|
||||
selected.split_whitespace().next().unwrap().to_string()
|
||||
});
|
||||
|
||||
if res.is_empty() {
|
||||
return select_gitmoji();
|
||||
}
|
||||
|
||||
res
|
||||
}
|
||||
|
||||
pub struct CommitMessage {
|
||||
gitmoji: String,
|
||||
title: String,
|
||||
body: Option<String>,
|
||||
}
|
||||
|
||||
impl CommitMessage {
|
||||
pub const fn new(gitmoji: String, title: String, body: Option<String>) -> Self {
|
||||
CommitMessage {
|
||||
gitmoji,
|
||||
title,
|
||||
body,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_msg(&self) -> String {
|
||||
let mut msg = self.gitmoji.clone();
|
||||
msg.push_str(&format!(" {}", self.title));
|
||||
|
||||
if let Some(body) = &self.body {
|
||||
if !body.is_empty() {
|
||||
msg.push_str(&format!("\n\n{body}"));
|
||||
}
|
||||
}
|
||||
|
||||
msg
|
||||
}
|
||||
}
|
154
src/main.rs
Normal file
154
src/main.rs
Normal file
|
@ -0,0 +1,154 @@
|
|||
use git::{bootstrap, commit, create_new_branch, delete_branch, fetch, git_add, pull, push};
|
||||
use gitmoji::{CommitMessage, select_gitmoji};
|
||||
use std::io::Read;
|
||||
use subprocess::{Exec, Redirection};
|
||||
|
||||
mod args;
|
||||
mod git;
|
||||
mod gitmoji;
|
||||
mod precommit;
|
||||
|
||||
pub fn read_stdout(e: Exec) -> String {
|
||||
let mut p = e.stdout(Redirection::Pipe).popen().unwrap();
|
||||
let mut str = String::new();
|
||||
p.stdout.as_mut().unwrap().read_to_string(&mut str).unwrap();
|
||||
str.trim().to_string()
|
||||
}
|
||||
|
||||
const TODO_REGEX: &str = r" (todo|unimplemented|refactor|wip|fix)(:|!|\n|\ :)";
|
||||
const NO_COMMIT_REGEX: &str = r"(NOCOMMIT|ENSURE: )";
|
||||
|
||||
pub fn no_commit_amount() -> u64 {
|
||||
rg_matches(NO_COMMIT_REGEX)
|
||||
}
|
||||
|
||||
pub fn todos_amount() -> u64 {
|
||||
rg_matches(TODO_REGEX)
|
||||
}
|
||||
|
||||
pub fn rg_matches(regex: &str) -> u64 {
|
||||
let mut cmd = Exec::cmd("rg")
|
||||
.arg("-i")
|
||||
.arg("--json")
|
||||
.arg("--multiline")
|
||||
.arg(regex)
|
||||
.stdout(Redirection::Pipe)
|
||||
.popen()
|
||||
.unwrap();
|
||||
cmd.wait().unwrap();
|
||||
let mut str = String::new();
|
||||
cmd.stdout
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.read_to_string(&mut str)
|
||||
.unwrap();
|
||||
let last_line = str.lines().last().unwrap();
|
||||
let val: serde_json::Value = serde_json::from_str(last_line).unwrap();
|
||||
let ret = (|| {
|
||||
val.as_object()
|
||||
.unwrap()
|
||||
.get("data")?
|
||||
.as_object()?
|
||||
.get("stats")?
|
||||
.as_object()?
|
||||
.get("matches")?
|
||||
.as_number()
|
||||
.unwrap()
|
||||
.as_i64()
|
||||
})();
|
||||
ret.unwrap_or(0) as u64
|
||||
}
|
||||
|
||||
pub fn show_rg(regex: &str) {
|
||||
Exec::cmd("rg")
|
||||
.arg("-i")
|
||||
.arg("--multiline")
|
||||
.arg(regex)
|
||||
.popen()
|
||||
.unwrap()
|
||||
.wait()
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
pub fn show_todos() {
|
||||
println!("{} todos found.", todos_amount());
|
||||
show_rg(TODO_REGEX);
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let args = args::get_args();
|
||||
|
||||
match args.subcommand() {
|
||||
Some(("bootstrap", bs_args)) => {
|
||||
let base = bs_args.get_one::<String>("BASE").unwrap();
|
||||
let repo = bs_args.get_one::<String>("NEW").unwrap();
|
||||
bootstrap(base, repo);
|
||||
}
|
||||
Some(("new", new_args)) => {
|
||||
let branch: &String = new_args.get_one("BRANCH").unwrap();
|
||||
create_new_branch(branch);
|
||||
}
|
||||
Some(("remove", rm_args)) => {
|
||||
let branch: &String = rm_args.get_one("BRANCH").unwrap();
|
||||
delete_branch(branch);
|
||||
}
|
||||
Some(("branch", _)) => {
|
||||
Exec::cmd("git")
|
||||
.arg("branch")
|
||||
.popen()
|
||||
.unwrap()
|
||||
.wait()
|
||||
.unwrap();
|
||||
}
|
||||
Some(("push", push_args)) => {
|
||||
let force = push_args.get_flag("force");
|
||||
push(force);
|
||||
}
|
||||
Some(("commit", commit_args)) => {
|
||||
let all = commit_args.get_flag("all");
|
||||
let done = commit_args.get_flag("done");
|
||||
let msg: Option<&String> = commit_args.get_one("message");
|
||||
let interactive = commit_args.get_flag("interactive");
|
||||
|
||||
if interactive || msg.is_none() {
|
||||
let gitmoji = select_gitmoji();
|
||||
let title = inquire::prompt_text("Commit Title").unwrap();
|
||||
let body = inquire::Text::new("Commit Description")
|
||||
.with_page_size(5)
|
||||
.prompt();
|
||||
let msg = CommitMessage::new(gitmoji, title, body.ok()).to_msg();
|
||||
commit(all, done, &msg);
|
||||
} else {
|
||||
commit(all, done, msg.unwrap());
|
||||
}
|
||||
}
|
||||
Some(("save", _)) => {
|
||||
commit(true, false, "WIP");
|
||||
}
|
||||
Some(("fetch", _)) => {
|
||||
fetch();
|
||||
}
|
||||
Some(("pull", _)) => {
|
||||
pull();
|
||||
}
|
||||
Some(("todo", _)) => {
|
||||
show_todos();
|
||||
}
|
||||
Some(("log", _)) => {
|
||||
Exec::cmd("serie").popen().unwrap().wait().unwrap();
|
||||
}
|
||||
Some(("status", _)) => {
|
||||
Exec::cmd("git")
|
||||
.arg("status")
|
||||
.popen()
|
||||
.unwrap()
|
||||
.wait()
|
||||
.unwrap();
|
||||
}
|
||||
Some(("add", args)) => {
|
||||
let file: &String = args.get_one("FILE").unwrap();
|
||||
git_add(file);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
22
src/precommit.rs
Normal file
22
src/precommit.rs
Normal file
|
@ -0,0 +1,22 @@
|
|||
use crate::{git::git_add, read_stdout};
|
||||
use subprocess::Exec;
|
||||
|
||||
pub fn rust_pre_commit() {
|
||||
println!("Running cargo fmt");
|
||||
let e = Exec::cmd("cargo").arg("fmt").arg("--verbose");
|
||||
let out = read_stdout(e);
|
||||
|
||||
for line in out.lines() {
|
||||
println!("line {line}");
|
||||
if line.starts_with("rustfmt") {
|
||||
let mut split: Vec<_> = line.split_whitespace().collect();
|
||||
split.reverse();
|
||||
split.pop();
|
||||
split.pop();
|
||||
split.pop();
|
||||
split.reverse();
|
||||
let file = split.join(" ");
|
||||
git_add(&file);
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue