changepacks_cli/
lib.rs

1use anyhow::Result;
2
3use changepacks_core::UpdateType;
4use clap::{Parser, Subcommand, ValueEnum};
5
6use crate::{
7    commands::{
8        ChangepackArgs, CheckArgs, ConfigArgs, InitArgs, PublishArgs, UpdateArgs,
9        handle_changepack, handle_check, handle_config, handle_init, handle_publish, handle_update,
10    },
11    options::FilterOptions,
12};
13pub mod commands;
14mod finders;
15pub mod options;
16pub mod prompter;
17
18pub use prompter::UserCancelled;
19
20#[derive(ValueEnum, Debug, Clone)]
21enum CliUpdateType {
22    Major,
23    Minor,
24    Patch,
25}
26
27impl From<CliUpdateType> for UpdateType {
28    fn from(value: CliUpdateType) -> Self {
29        match value {
30            CliUpdateType::Major => UpdateType::Major,
31            CliUpdateType::Minor => UpdateType::Minor,
32            CliUpdateType::Patch => UpdateType::Patch,
33        }
34    }
35}
36
37#[derive(Parser, Debug)]
38#[command(author, version, about = "changepacks CLI")]
39struct Cli {
40    #[command(subcommand)]
41    command: Option<Commands>,
42
43    #[arg(short, long)]
44    filter: Option<FilterOptions>,
45
46    #[arg(short, long, default_value = "false")]
47    remote: bool,
48
49    #[arg(short, long, default_value = "false")]
50    yes: bool,
51
52    #[arg(short, long)]
53    message: Option<String>,
54
55    #[arg(short, long)]
56    update_type: Option<CliUpdateType>,
57}
58
59#[derive(Subcommand, Debug)]
60enum Commands {
61    Init(InitArgs),
62    Check(CheckArgs),
63    Update(UpdateArgs),
64    Config(ConfigArgs),
65    Publish(PublishArgs),
66}
67
68pub async fn main(args: &[String]) -> Result<()> {
69    let cli = Cli::parse_from(args);
70    if let Some(command) = cli.command {
71        match command {
72            Commands::Init(args) => handle_init(&args).await?,
73            Commands::Check(args) => handle_check(&args).await?,
74            Commands::Update(args) => handle_update(&args).await?,
75            Commands::Config(args) => handle_config(&args).await?,
76            Commands::Publish(args) => handle_publish(&args).await?,
77        }
78    } else {
79        handle_changepack(&ChangepackArgs {
80            filter: cli.filter,
81            remote: cli.remote,
82            yes: cli.yes,
83            message: cli.message,
84            update_type: cli.update_type.map(Into::into),
85        })
86        .await?;
87    }
88    Ok(())
89}
90
91#[cfg(test)]
92mod tests {
93    use super::*;
94    use rstest::rstest;
95
96    #[rstest]
97    #[case(CliUpdateType::Major, UpdateType::Major)]
98    #[case(CliUpdateType::Minor, UpdateType::Minor)]
99    #[case(CliUpdateType::Patch, UpdateType::Patch)]
100    fn test_cli_update_type_to_update_type(
101        #[case] cli_type: CliUpdateType,
102        #[case] expected: UpdateType,
103    ) {
104        let result: UpdateType = cli_type.into();
105        assert_eq!(result, expected);
106    }
107
108    // Test that Cli struct parses correctly
109    #[test]
110    fn test_cli_parsing_init() {
111        use clap::Parser;
112        let cli = Cli::parse_from(["changepacks", "init"]);
113        assert!(matches!(cli.command, Some(Commands::Init(_))));
114    }
115
116    #[test]
117    fn test_cli_parsing_check() {
118        use clap::Parser;
119        let cli = Cli::parse_from(["changepacks", "check"]);
120        assert!(matches!(cli.command, Some(Commands::Check(_))));
121    }
122
123    #[test]
124    fn test_cli_parsing_update() {
125        use clap::Parser;
126        let cli = Cli::parse_from(["changepacks", "update", "--dry-run"]);
127        assert!(matches!(cli.command, Some(Commands::Update(_))));
128    }
129
130    #[test]
131    fn test_cli_parsing_config() {
132        use clap::Parser;
133        let cli = Cli::parse_from(["changepacks", "config"]);
134        assert!(matches!(cli.command, Some(Commands::Config(_))));
135    }
136
137    #[test]
138    fn test_cli_parsing_publish() {
139        use clap::Parser;
140        let cli = Cli::parse_from(["changepacks", "publish", "--dry-run"]);
141        assert!(matches!(cli.command, Some(Commands::Publish(_))));
142    }
143
144    #[test]
145    fn test_cli_parsing_default_with_options() {
146        use clap::Parser;
147        let cli = Cli::parse_from([
148            "changepacks",
149            "--yes",
150            "--message",
151            "test",
152            "--update-type",
153            "patch",
154        ]);
155        assert!(cli.command.is_none());
156        assert!(cli.yes);
157        assert_eq!(cli.message, Some("test".to_string()));
158        assert!(matches!(cli.update_type, Some(CliUpdateType::Patch)));
159    }
160
161    #[test]
162    fn test_cli_parsing_with_filter() {
163        use clap::Parser;
164        let cli = Cli::parse_from(["changepacks", "--filter", "package"]);
165        assert!(cli.command.is_none());
166        assert!(matches!(cli.filter, Some(FilterOptions::Package)));
167    }
168
169    #[test]
170    fn test_cli_parsing_with_remote() {
171        use clap::Parser;
172        let cli = Cli::parse_from(["changepacks", "--remote"]);
173        assert!(cli.remote);
174    }
175}