git_mob_tool/
cli.rs

1use crate::coauthor_repo::CoauthorRepo;
2use crate::commands::{mob::Mob, setup::Setup, team_member::TeamMember};
3use clap::{Parser, Subcommand};
4use std::error::Error;
5use std::io::Write;
6use std::str;
7
8#[derive(Parser)]
9#[command(
10    author,
11    version,
12    about,
13    long_about,
14    bin_name = "git mob",
15    override_usage = "git mob [COMMAND] [OPTIONS]"
16)]
17#[command(propagate_version = true)]
18/// A CLI app which can help users automatically add co-author(s) to git commits
19/// for pair/mob programming.
20///
21/// A user can attribute a git commit to more than one author by adding one or more
22/// Co-authored-by trailers to the commit's message.
23/// Co-authored commits are visible on GitHub.
24/// This CLI app will helper users do the this automatically and also help them
25/// store and manage co-authors for pair/mob programming sessions.
26///
27/// Usage example:
28///
29/// git mob setup --global
30///
31/// git mob team-member --add lm "Leo Messi" leo.messi@example.com
32///
33/// git mob --with lm
34struct Cli {
35    #[command(subcommand)]
36    command: Option<Commands>,
37    #[command(flatten)]
38    mob: Mob,
39}
40
41#[derive(Subcommand)]
42enum Commands {
43    /// Create prepare-commit-msg githook which append Co-authored-by trailers to commit message
44    Setup(Setup),
45    /// Add/delete/list team member(s) from team member repository
46    ///
47    /// User must store team member(s) to team member repository by using keys
48    /// before starting pair/mob programming session(s).
49    #[clap(alias = "coauthor")] // alias for backward compatibility
50    TeamMember(TeamMember),
51}
52
53pub fn run(coauthor_repo: &impl CoauthorRepo, out: &mut impl Write) -> Result<(), Box<dyn Error>> {
54    let cli = Cli::parse();
55    run_inner(&cli, coauthor_repo, out)
56}
57
58fn run_inner(
59    cli: &Cli,
60    coauthor_repo: &impl CoauthorRepo,
61    out: &mut impl Write,
62) -> Result<(), Box<dyn Error>> {
63    match &cli.command {
64        None => cli.mob.handle(coauthor_repo, out)?,
65        Some(Commands::Setup(setup)) => setup.handle(out)?,
66        Some(Commands::TeamMember(team_member)) => team_member.handle(coauthor_repo, out)?,
67    }
68    Ok(())
69}
70
71#[cfg(test)]
72mod tests {
73    use std::error::Error;
74
75    use super::*;
76    use crate::coauthor_repo::MockCoauthorRepo;
77    use mockall::predicate;
78
79    #[test]
80    fn test_clear_mob_session() -> Result<(), Box<dyn Error>> {
81        let mut mock_coauthor_repo = MockCoauthorRepo::new();
82        mock_coauthor_repo
83            .expect_clear_mob()
84            .once()
85            .returning(|| Ok(()));
86
87        let cli = Cli {
88            command: None,
89            mob: Mob {
90                with: None,
91                clear: true,
92                list: false,
93                trailers: false,
94            },
95        };
96
97        let mut out = Vec::new();
98        run_inner(&cli, &mock_coauthor_repo, &mut out)?;
99
100        Ok(())
101    }
102
103    #[test]
104    fn test_delete_team_member() -> Result<(), Box<dyn Error>> {
105        let key = "lm";
106        let mut mock_coauthor_repo = MockCoauthorRepo::new();
107        mock_coauthor_repo
108            .expect_get()
109            .with(predicate::eq(key))
110            .once()
111            .returning(|_| Ok(Some("Leo Messi <leo.messi@example.com>".to_owned())));
112        mock_coauthor_repo
113            .expect_remove()
114            .with(predicate::eq(key))
115            .once()
116            .returning(|_| Ok(()));
117
118        let cli = Cli {
119            command: Some(Commands::TeamMember(TeamMember {
120                delete: Some(key.to_owned()),
121                add: None,
122                list: false,
123            })),
124            mob: Mob {
125                with: None,
126                clear: false,
127                list: false,
128                trailers: false,
129            },
130        };
131
132        let mut out = Vec::new();
133        run_inner(&cli, &mock_coauthor_repo, &mut out)?;
134
135        Ok(())
136    }
137}