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]
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}