1macro_rules! vprintln {
4 ($($arg:tt)*) => {
5 if $crate::verbose() {
6 println!($($arg)*);
7 }
8 };
9}
10
11pub mod commands;
12pub mod hmm;
13
14use std::path::PathBuf;
15use std::sync::atomic::{AtomicBool, Ordering};
16
17use anyhow::{Ok, Result};
18
19use clap::{Args, CommandFactory, Parser, Subcommand, ValueEnum};
20use shadow_rs::shadow;
21
22use crate::commands::add_command;
23
24shadow!(build);
25
26#[derive(Parser, Debug)]
27#[command(author, version, about, long_about = None)]
28#[command(version = build::CLAP_LONG_VERSION)]
29struct Cli {
30 #[command(subcommand)]
31 cmd: Commands,
32
33 #[command(flatten)]
34 global_opts: GlobalOpts,
35}
36
37#[derive(Debug, Args, Clone)]
40pub struct LibraryFilter {
41 #[arg(value_name = "LIBS")]
43 pub lib: Vec<String>,
44}
45
46#[derive(Subcommand, Debug, Clone)]
47enum Commands {
48 #[command(visible_alias = "ls")]
51 List {
52 #[command(flatten)]
53 filter: LibraryFilter,
54 },
55 Init,
57 #[command(visible_alias = "cl")]
59 Clean,
60 ToHxml {
62 #[arg(value_name = "HXML")]
64 hxml: Option<PathBuf>,
65 },
66 #[command(visible_alias = "ch")]
69 Check {
70 #[command(flatten)]
71 filter: LibraryFilter,
72 },
73 #[command(visible_alias = "i")]
76 Install {
77 #[command(flatten)]
78 filter: LibraryFilter,
79 },
80 Add(AddArgs),
81 Haxelib {
83 #[arg(required = true, num_args = 1..)]
85 names: Vec<String>,
86 },
87 Git {
89 name: String,
91 url: String,
93 #[arg(value_name = "REF")]
95 git_ref: Option<String>,
96 #[arg(value_name = "DIR")]
98 dir: Option<String>,
99 },
100 #[command(visible_alias = "rm")]
102 Remove {
103 #[command(flatten)]
104 filter: LibraryFilter,
105 },
106 Dev {
108 name: String,
110 path: String,
112 },
113 #[command(visible_alias = "self-update")]
115 Upgrade {
116 #[arg(long)]
118 check: bool,
119 },
120 Lock {
122 #[command(subcommand)]
123 subcommand: Option<LockCommands>,
124
125 #[arg(short = 's', long = "short-id")]
127 short_id: bool,
128
129 #[command(flatten)]
130 filter: LibraryFilter,
131 },
132 Completions {
134 shell: clap_complete::Shell,
136 },
137}
138
139#[derive(Debug, Args, Clone)]
140pub struct AddArgs {
141 #[arg(required = true, num_args = 1..)]
143 pub names: Vec<String>,
144 #[arg(long, value_name = "URL")]
145 pub git: Option<String>,
146 #[arg(long = "ref", value_name = "REF")]
148 pub git_ref: Option<String>,
149 #[arg(long = "dir", value_name = "DIR")]
151 pub dir: Option<String>,
152}
153
154#[derive(Debug, Args)]
155struct GlobalOpts {
156 #[arg(long, value_enum, global = true, default_value_t = Color::Auto)]
158 color: Color,
159 #[arg(short, long, value_name = "JSON", default_value = "hmm.json")]
161 json: Option<PathBuf>,
162 #[arg(long, short, global = true, action = clap::ArgAction::Count)]
164 verbose: u8,
165 #[arg(long, global = true, value_name = "SEP")]
168 remote_separator: Option<String>,
169 }
171
172#[derive(Clone, Debug, ValueEnum)]
173enum Color {
174 Always,
175 Auto,
176 Never,
177}
178
179#[derive(Subcommand, Debug, Clone)]
180enum LockCommands {
181 Check,
183}
184
185static VERBOSE: AtomicBool = AtomicBool::new(false);
186
187pub fn verbose() -> bool {
189 VERBOSE.load(Ordering::Relaxed)
190}
191
192pub fn run() -> Result<()> {
193 let args = Cli::parse();
194 VERBOSE.store(args.global_opts.verbose > 0, Ordering::Relaxed);
195
196 let path = args.global_opts.json.clone().unwrap();
197 let load_deps = || hmm::json::read_json(&path);
198 let remote_separator = commands::install_command::resolve_remote_separator(
199 args.global_opts.remote_separator.as_deref(),
200 );
201
202 match args.cmd {
203 Commands::Add(add_args) => {
204 add_command::add_dependency(add_args, load_deps()?, path, &remote_separator)?
205 }
206 Commands::List { filter } => hmm::json::read_json(&path)?.print_string_list(&filter.lib)?,
207 Commands::Init => commands::init_command::init_hmm()?,
208 Commands::Clean => commands::clean_command::remove_haxelib_folder()?,
209 Commands::ToHxml { hxml } => commands::tohxml_command::dump_to_hxml(&load_deps()?, hxml)?,
210 Commands::Check { filter } => commands::check_command::check(
211 &load_deps()?,
212 &filter.lib,
213 args.global_opts.verbose > 0,
214 )?,
215 Commands::Install { filter } => commands::install_command::install_from_hmm(
216 &load_deps()?,
217 &filter.lib,
218 &remote_separator,
219 &path,
220 )?,
221 Commands::Haxelib { names } => commands::haxelib_command::install_haxelibs(&names, path)?,
222 Commands::Git {
223 name,
224 url,
225 git_ref,
226 dir,
227 } => commands::git_command::install_git(
228 &name,
229 &url,
230 &git_ref,
231 &dir,
232 load_deps()?,
233 path,
234 &remote_separator,
235 )?,
236 Commands::Remove { filter } => {
237 commands::remove_command::remove_haxelibs(load_deps()?, &filter.lib, path)?
238 }
239 Commands::Upgrade { check } => commands::upgrade_command::upgrade(check)?,
240 Commands::Dev { name, path } => commands::dev_command::add_dev_dependency(
241 &name,
242 &path,
243 args.global_opts.json.clone().unwrap(),
244 )?,
245 Commands::Lock {
246 subcommand,
247 short_id,
248 filter,
249 } => match subcommand {
250 Some(LockCommands::Check) => commands::lock_command::check_locked(&load_deps()?)?,
251 None => commands::lock_command::lock_dependencies(
252 &load_deps()?,
253 &filter.lib,
254 args.global_opts.json.unwrap(),
255 short_id,
256 )?,
257 },
258 Commands::Completions { shell } => {
259 let mut cmd = Cli::command();
260 let name = cmd.get_name().to_string();
261 clap_complete::generate(shell, &mut cmd, name, &mut std::io::stdout());
262 }
263 }
264 Ok(())
265}
266
267#[test]
268fn verify_cli() {
269 Cli::command().debug_assert();
270}