feat: Merge all functionality into one binary
This groups all entrypoints into one `git-mob` binary. One of the main questions I've gotten about this library is why the `add-coauthor` (and similar) aren't an option or a subcommand to `git-mob`. I've also come to the conclusion that it's a bit confusing to have to memorize five different binaries rather than one, since they're all related anyway.
This commit is contained in:
parent
ee5b90653b
commit
20c68a3b14
44
build.rs
44
build.rs
|
|
@ -1,31 +1,29 @@
|
||||||
use clap::CommandFactory;
|
// use clap::CommandFactory;
|
||||||
use clap_mangen::Man;
|
// use clap_mangen::Man;
|
||||||
use std::env;
|
// use std::env;
|
||||||
use std::path::Path;
|
// use std::path::Path;
|
||||||
|
|
||||||
#[path = "src/cli.rs"]
|
|
||||||
mod cli;
|
|
||||||
|
|
||||||
macro_rules! generate_manpage {
|
// macro_rules! generate_manpage {
|
||||||
($struct:ident) => {
|
// ($struct:ident) => {
|
||||||
let target_dir = env::var("CARGO_TARGET_DIR").unwrap_or("target".to_string());
|
// let target_dir = env::var("CARGO_TARGET_DIR").unwrap_or("target".to_string());
|
||||||
let output_dir = Path::new(&target_dir).join(env::var("PROFILE").unwrap());
|
// let output_dir = Path::new(&target_dir).join(env::var("PROFILE").unwrap());
|
||||||
|
|
||||||
let cmd = cli::$struct::command();
|
// let cmd = cli::$struct::command();
|
||||||
let cmd_name = format!("{}.1", cmd.get_name());
|
// let cmd_name = format!("{}.1", cmd.get_name());
|
||||||
let man = Man::new(cmd);
|
// let man = Man::new(cmd);
|
||||||
let mut buffer: Vec<u8> = Default::default();
|
// let mut buffer: Vec<u8> = Default::default();
|
||||||
man.render(&mut buffer)?;
|
// man.render(&mut buffer)?;
|
||||||
std::fs::write(output_dir.join(cmd_name), buffer)?;
|
// std::fs::write(output_dir.join(cmd_name), buffer)?;
|
||||||
};
|
// };
|
||||||
}
|
// }
|
||||||
|
|
||||||
fn main() -> std::io::Result<()> {
|
fn main() -> std::io::Result<()> {
|
||||||
generate_manpage!(GitMob);
|
// generate_manpage!(GitMob);
|
||||||
generate_manpage!(GitSolo);
|
// generate_manpage!(GitSolo);
|
||||||
generate_manpage!(GitAddCoauthor);
|
// generate_manpage!(GitAddCoauthor);
|
||||||
generate_manpage!(GitEditCoauthor);
|
// generate_manpage!(GitEditCoauthor);
|
||||||
generate_manpage!(GitDeleteCoauthor);
|
// generate_manpage!(GitDeleteCoauthor);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,15 +0,0 @@
|
||||||
use clap::Parser;
|
|
||||||
use git_mob::{cli, parse_coauthors_file, write_coauthors_file, Author};
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
let opt = cli::GitAddCoauthor::parse();
|
|
||||||
let mut authors = parse_coauthors_file().unwrap_or_default();
|
|
||||||
let new_author = Author {
|
|
||||||
name: opt.name,
|
|
||||||
email: opt.email,
|
|
||||||
};
|
|
||||||
|
|
||||||
authors.insert(opt.initials, new_author);
|
|
||||||
|
|
||||||
write_coauthors_file(authors);
|
|
||||||
}
|
|
||||||
|
|
@ -1,9 +0,0 @@
|
||||||
use clap::Parser;
|
|
||||||
use git_mob::{cli, get_available_coauthors, write_coauthors_file};
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
let opt = cli::GitDeleteCoauthor::parse();
|
|
||||||
let mut authors = get_available_coauthors();
|
|
||||||
authors.remove(&opt.initials);
|
|
||||||
write_coauthors_file(authors);
|
|
||||||
}
|
|
||||||
|
|
@ -1,22 +0,0 @@
|
||||||
use clap::Parser;
|
|
||||||
use git_mob::{cli, get_available_coauthors, write_coauthors_file, Author};
|
|
||||||
use std::process;
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
let opt = cli::GitDeleteCoauthor::parse();
|
|
||||||
|
|
||||||
let mut authors = get_available_coauthors();
|
|
||||||
|
|
||||||
if let Some(author) = authors.get(&opt.initials) {
|
|
||||||
let mut updated_author: Author = author.clone();
|
|
||||||
updated_author.name = opt.name;
|
|
||||||
updated_author.email = opt.email;
|
|
||||||
|
|
||||||
authors.insert(opt.initials, updated_author);
|
|
||||||
|
|
||||||
write_coauthors_file(authors);
|
|
||||||
} else {
|
|
||||||
eprintln!("No author found with initials {}", &opt.initials);
|
|
||||||
process::exit(1);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
@ -1,79 +0,0 @@
|
||||||
use clap::Parser;
|
|
||||||
use git_mob::{
|
|
||||||
cli, ensure_commit_template_is_set, get_available_coauthors, get_main_author, set_main_author,
|
|
||||||
with_gitmessage_template_path_or_exit, Author,
|
|
||||||
};
|
|
||||||
use std::fmt::Write;
|
|
||||||
use std::fs;
|
|
||||||
use std::process;
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
let args = cli::GitMob::parse();
|
|
||||||
|
|
||||||
if args.list {
|
|
||||||
list_coauthors();
|
|
||||||
process::exit(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(initials) = args.overwrite {
|
|
||||||
override_main_author(&initials);
|
|
||||||
}
|
|
||||||
|
|
||||||
write_coauthors_to_gitmessage_file(&args.coauthors);
|
|
||||||
ensure_commit_template_is_set();
|
|
||||||
}
|
|
||||||
|
|
||||||
fn list_coauthors() {
|
|
||||||
for (abbrev, author) in &get_available_coauthors() {
|
|
||||||
println!("{abbrev}\t{author}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn override_main_author(initials: &str) {
|
|
||||||
let all_authors = get_available_coauthors();
|
|
||||||
match all_authors.get(initials) {
|
|
||||||
Some(new_main_author) => set_main_author(new_main_author),
|
|
||||||
None => {
|
|
||||||
eprintln!("Error: author with initials {initials} not found");
|
|
||||||
process::exit(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn write_coauthors_to_gitmessage_file(coauthor_initials: &[String]) {
|
|
||||||
let coauthors = select_coauthors(coauthor_initials);
|
|
||||||
let mut content = String::from("\n\n");
|
|
||||||
for author in &coauthors {
|
|
||||||
_ = writeln!(content, "Co-authored-by: {}", &author.to_string());
|
|
||||||
}
|
|
||||||
|
|
||||||
with_gitmessage_template_path_or_exit(|path| match fs::write(path, content) {
|
|
||||||
Ok(_) => {
|
|
||||||
println!("{}", get_main_author());
|
|
||||||
for author in &coauthors {
|
|
||||||
println!("{author}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Err(e) => {
|
|
||||||
eprintln!("Error writing to .gitmessage template: {e}");
|
|
||||||
process::exit(1);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
fn select_coauthors(coauthor_initials: &[String]) -> Vec<Author> {
|
|
||||||
let all_coauthors = get_available_coauthors();
|
|
||||||
let mut coauthors: Vec<Author> = Vec::new();
|
|
||||||
|
|
||||||
for initial in coauthor_initials {
|
|
||||||
match all_coauthors.get(initial) {
|
|
||||||
Some(coauthor) => coauthors.push(coauthor.clone()),
|
|
||||||
None => {
|
|
||||||
eprintln!("Error: author with initials {initial} not found");
|
|
||||||
process::exit(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
coauthors
|
|
||||||
}
|
|
||||||
|
|
@ -1,16 +0,0 @@
|
||||||
use clap::Parser;
|
|
||||||
use git_mob::{
|
|
||||||
cli, ensure_commit_template_is_set, get_main_author, with_gitmessage_template_path_or_exit,
|
|
||||||
};
|
|
||||||
use std::fs::File;
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
let _opt = cli::GitSolo::parse();
|
|
||||||
let main_author = get_main_author();
|
|
||||||
println!("{main_author}");
|
|
||||||
|
|
||||||
with_gitmessage_template_path_or_exit(|path| {
|
|
||||||
let _template = File::create(path);
|
|
||||||
});
|
|
||||||
ensure_commit_template_is_set();
|
|
||||||
}
|
|
||||||
98
src/cli.rs
98
src/cli.rs
|
|
@ -1,56 +1,46 @@
|
||||||
use clap::Parser;
|
use clap::{Arg, arg};
|
||||||
|
|
||||||
#[derive(Parser, Debug)]
|
pub fn git_mob_cmd() -> clap::Command {
|
||||||
#[clap(version, name = "git-mob")]
|
clap::Command::new("git-mob")
|
||||||
/// Assemble a group of co-authors to help you on your coding quest
|
.bin_name("git-mob")
|
||||||
pub struct GitMob {
|
.author("Martin Frost, martin@frost.codes")
|
||||||
/// Prints list of available co-authors
|
.version("0.0.0")
|
||||||
#[clap(short, long)]
|
.about("A command-line tool for social coding")
|
||||||
pub list: bool,
|
.subcommand(
|
||||||
/// Overwrite the main author
|
clap::Command::new("add")
|
||||||
#[clap(short, long)]
|
.alias("add-coauthor")
|
||||||
pub overwrite: Option<String>,
|
.about("Add a coauthor to the database")
|
||||||
/// A list of co-author initials
|
.arg(arg!(<handle> "The coauthor's handle"))
|
||||||
pub coauthors: Vec<String>,
|
.arg(arg!(<name> "The coauthor's name, in quotes"))
|
||||||
|
.arg(arg!(<email> "The coauthor's email"))
|
||||||
|
)
|
||||||
|
.subcommand(
|
||||||
|
clap::Command::new("edit")
|
||||||
|
.alias("edit-coauthor")
|
||||||
|
.about("Edit a coauthor in the database")
|
||||||
|
.arg(arg!(<handle> "The coauthor's handle"))
|
||||||
|
.arg(arg!(<name> "The coauthor's name, in quotes"))
|
||||||
|
.arg(arg!(<email> "The coauthor's email"))
|
||||||
|
)
|
||||||
|
.subcommand(
|
||||||
|
clap::Command::new("remove")
|
||||||
|
.alias("remove-coauthor")
|
||||||
|
.alias("rm")
|
||||||
|
.about("Remove a coauthor from the database")
|
||||||
|
.arg(arg!(<handle> "The coauthor's handle"))
|
||||||
|
)
|
||||||
|
.subcommand(
|
||||||
|
clap::Command::new("solo")
|
||||||
|
.about("Disband the mob. Continue coding on your own")
|
||||||
|
.arg_required_else_help(false))
|
||||||
|
.subcommand(
|
||||||
|
clap::Command::new("list")
|
||||||
|
.about("List available coauthors in the database")
|
||||||
|
.arg_required_else_help(false))
|
||||||
|
.subcommand(
|
||||||
|
clap::Command::new("with")
|
||||||
|
.about("Choose coauthors to join your mob")
|
||||||
|
.arg_required_else_help(true)
|
||||||
|
.arg(Arg::new("override").short('o').long("override").help("Override default author").value_name("handle"))
|
||||||
|
.arg(arg!([handles] "The coauthors you want in your mob").num_args(1..)))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Parser, Debug)]
|
|
||||||
#[clap(name = "git-add-coauthor", version)]
|
|
||||||
/// Add a co-author to your list of available co-authors
|
|
||||||
pub struct GitAddCoauthor {
|
|
||||||
/// Co-author initials
|
|
||||||
pub initials: String,
|
|
||||||
/// The name of the co-author, in quotes, e.g. "Foo Bar"
|
|
||||||
pub name: String,
|
|
||||||
/// The email of the co-author
|
|
||||||
pub email: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Parser, Debug)]
|
|
||||||
#[clap(name = "git-edit-coauthor", version)]
|
|
||||||
/// Edit a co-author in your list of available co-authors
|
|
||||||
pub struct GitEditCoauthor {
|
|
||||||
/// Co-author initials
|
|
||||||
pub initials: String,
|
|
||||||
/// The name of the co-author, in quotes, e.g. "Foo Bar"
|
|
||||||
pub name: String,
|
|
||||||
/// The email of the co-author
|
|
||||||
pub email: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Parser, Debug)]
|
|
||||||
#[clap(name = "git-delete-coauthor", version)]
|
|
||||||
/// Delete a co-author from your list of available co-authors
|
|
||||||
pub struct GitDeleteCoauthor {
|
|
||||||
/// Co-author initials
|
|
||||||
pub initials: String,
|
|
||||||
/// The name of the co-author, in quotes, e.g. "Foo Bar"
|
|
||||||
pub name: String,
|
|
||||||
/// The email of the co-author
|
|
||||||
pub email: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Parser, Debug)]
|
|
||||||
#[clap(name = "git-solo", version)]
|
|
||||||
/// Disband the mob and continue working solo.
|
|
||||||
pub struct GitSolo {}
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,195 @@
|
||||||
|
use clap::{Arg, arg};
|
||||||
|
use git_mob::{
|
||||||
|
ensure_commit_template_is_set, get_available_coauthors,
|
||||||
|
get_main_author, set_main_author, with_gitmessage_template_path_or_exit,
|
||||||
|
Author, parse_coauthors_file, write_coauthors_file
|
||||||
|
};
|
||||||
|
use std::fmt::Write;
|
||||||
|
use std::fs::File;
|
||||||
|
use std::fs;
|
||||||
|
use std::process;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let matches = git_mob_cmd().get_matches();
|
||||||
|
|
||||||
|
match matches.subcommand_name() {
|
||||||
|
Some("add") => {
|
||||||
|
let args = matches.subcommand_matches("add").unwrap();
|
||||||
|
let handle = args.get_one("handle").unwrap();
|
||||||
|
let name = args.get_one("name").unwrap();
|
||||||
|
let email = args.get_one("email").unwrap();
|
||||||
|
add_coauthor(handle, name, email);
|
||||||
|
},
|
||||||
|
Some("edit") => {
|
||||||
|
let args = matches.subcommand_matches("edit").unwrap();
|
||||||
|
let handle = args.get_one("handle").unwrap();
|
||||||
|
let name = args.get_one("name").unwrap();
|
||||||
|
let email = args.get_one("email").unwrap();
|
||||||
|
edit_coauthor(handle, name, email);
|
||||||
|
},
|
||||||
|
Some("remove") => {
|
||||||
|
let handle = matches.subcommand_matches("remove").unwrap()
|
||||||
|
.get_one("handle").unwrap();
|
||||||
|
remove_coauthor(handle);
|
||||||
|
},
|
||||||
|
Some("list") => list_coauthors(),
|
||||||
|
Some("solo") => solo(),
|
||||||
|
Some("with") => {
|
||||||
|
let matches = matches.subcommand_matches("with").unwrap();
|
||||||
|
let handles: Vec<String> =
|
||||||
|
matches.get_many::<String>("handles").unwrap().map(|s| s.clone()).collect();
|
||||||
|
|
||||||
|
if let Some(handle) = matches.get_one::<String>("override") {
|
||||||
|
override_main_author(handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
write_coauthors_to_gitmessage_file(&handles);
|
||||||
|
},
|
||||||
|
_ => println!("Something else"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn git_mob_cmd() -> clap::Command {
|
||||||
|
clap::Command::new("git-mob")
|
||||||
|
.bin_name("git-mob")
|
||||||
|
.author("Martin Frost, martin@frost.codes")
|
||||||
|
.version("0.0.0")
|
||||||
|
.about("A command-line tool for social coding")
|
||||||
|
.subcommand(
|
||||||
|
clap::Command::new("add")
|
||||||
|
.alias("add-coauthor")
|
||||||
|
.about("Add a coauthor to the database")
|
||||||
|
.arg(arg!(<handle> "The coauthor's handle"))
|
||||||
|
.arg(arg!(<name> "The coauthor's name, in quotes"))
|
||||||
|
.arg(arg!(<email> "The coauthor's email"))
|
||||||
|
)
|
||||||
|
.subcommand(
|
||||||
|
clap::Command::new("edit")
|
||||||
|
.alias("edit-coauthor")
|
||||||
|
.about("Edit a coauthor in the database")
|
||||||
|
.arg(arg!(<handle> "The coauthor's handle"))
|
||||||
|
.arg(arg!(<name> "The coauthor's name, in quotes"))
|
||||||
|
.arg(arg!(<email> "The coauthor's email"))
|
||||||
|
)
|
||||||
|
.subcommand(
|
||||||
|
clap::Command::new("remove")
|
||||||
|
.alias("remove-coauthor")
|
||||||
|
.alias("rm")
|
||||||
|
.about("Remove a coauthor from the database")
|
||||||
|
.arg(arg!(<handle> "The coauthor's handle"))
|
||||||
|
)
|
||||||
|
.subcommand(
|
||||||
|
clap::Command::new("solo")
|
||||||
|
.about("Disband the mob. Continue coding on your own")
|
||||||
|
.arg_required_else_help(false))
|
||||||
|
.subcommand(
|
||||||
|
clap::Command::new("list")
|
||||||
|
.about("List available coauthors in the database")
|
||||||
|
.arg_required_else_help(false))
|
||||||
|
.subcommand(
|
||||||
|
clap::Command::new("with")
|
||||||
|
.about("Choose coauthors to join your mob")
|
||||||
|
.arg_required_else_help(true)
|
||||||
|
.arg(Arg::new("override").short('o').long("override").help("Override default author").value_name("handle"))
|
||||||
|
.arg(arg!([handles] "The coauthors you want in your mob").num_args(1..)))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn list_coauthors() {
|
||||||
|
for (abbrev, author) in &get_available_coauthors() {
|
||||||
|
println!("{abbrev}\t{author}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add_coauthor(handle: &String, name: &String, email: &String) {
|
||||||
|
let mut authors = parse_coauthors_file().unwrap_or_default();
|
||||||
|
let new_author = Author {
|
||||||
|
name: name.to_string(),
|
||||||
|
email: email.to_string(),
|
||||||
|
};
|
||||||
|
|
||||||
|
authors.insert(handle.to_string(), new_author);
|
||||||
|
|
||||||
|
write_coauthors_file(authors);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn edit_coauthor(handle: &String, name: &String, email: &String) {
|
||||||
|
let mut authors = get_available_coauthors();
|
||||||
|
|
||||||
|
if let Some(author) = authors.get(handle) {
|
||||||
|
let mut updated_author: Author = author.clone();
|
||||||
|
updated_author.name = name.to_string();
|
||||||
|
updated_author.email = email.to_string();
|
||||||
|
|
||||||
|
authors.insert(handle.to_string(), updated_author);
|
||||||
|
|
||||||
|
write_coauthors_file(authors);
|
||||||
|
} else {
|
||||||
|
eprintln!("No author found with initials {}", handle);
|
||||||
|
process::exit(1);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
fn remove_coauthor(handle: &String) {
|
||||||
|
let mut authors = get_available_coauthors();
|
||||||
|
authors.remove(handle);
|
||||||
|
write_coauthors_file(authors);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn solo() {
|
||||||
|
let main_author = get_main_author();
|
||||||
|
println!("{main_author}");
|
||||||
|
|
||||||
|
with_gitmessage_template_path_or_exit(|path| {
|
||||||
|
let _template = File::create(path);
|
||||||
|
});
|
||||||
|
ensure_commit_template_is_set();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn override_main_author(initials: &str) {
|
||||||
|
let all_authors = get_available_coauthors();
|
||||||
|
match all_authors.get(initials) {
|
||||||
|
Some(new_main_author) => set_main_author(new_main_author),
|
||||||
|
None => {
|
||||||
|
eprintln!("Error: author with initials {initials} not found");
|
||||||
|
process::exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_coauthors_to_gitmessage_file(coauthor_initials: &[String]) {
|
||||||
|
let coauthors = select_coauthors(coauthor_initials);
|
||||||
|
let mut content = String::from("\n\n");
|
||||||
|
for author in &coauthors {
|
||||||
|
_ = writeln!(content, "Co-authored-by: {}", &author.to_string());
|
||||||
|
}
|
||||||
|
|
||||||
|
with_gitmessage_template_path_or_exit(|path| match fs::write(path, content) {
|
||||||
|
Ok(_) => {
|
||||||
|
println!("{}", get_main_author());
|
||||||
|
for author in &coauthors {
|
||||||
|
println!("{author}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
eprintln!("Error writing to .gitmessage template: {e}");
|
||||||
|
process::exit(1);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn select_coauthors(coauthor_initials: &[String]) -> Vec<Author> {
|
||||||
|
let all_coauthors = get_available_coauthors();
|
||||||
|
let mut coauthors: Vec<Author> = Vec::new();
|
||||||
|
|
||||||
|
for initial in coauthor_initials {
|
||||||
|
match all_coauthors.get(initial) {
|
||||||
|
Some(coauthor) => coauthors.push(coauthor.clone()),
|
||||||
|
None => {
|
||||||
|
eprintln!("Error: author with initials {initial} not found");
|
||||||
|
process::exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
coauthors
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue