Skip to content

Commit 43eddab

Browse files
authored
cleaner cli (#18)
1 parent 0e3b741 commit 43eddab

File tree

3 files changed

+96
-32
lines changed

3 files changed

+96
-32
lines changed

Cargo.toml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "godot-package-manager"
3-
version = "0.8.0"
3+
version = "0.9.0"
44
edition = "2021"
55
authors = ["bendn <[email protected]>"]
66
description = "A package manager for godot"
@@ -22,6 +22,8 @@ serde_yaml = "0.9.14"
2222
tar = "0.4.38"
2323
toml = "0.5.10"
2424
sha1 = "0.10.5"
25+
console = "0.15.4"
26+
indicatif = "0.17.2"
2527

2628
[dev-dependencies]
2729
glob = "0.3.0"

src/main.rs

Lines changed: 86 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,16 @@ mod config_file;
22
mod package;
33

44
use crate::package::Package;
5-
use clap::{Parser, Subcommand, ValueEnum};
5+
use clap::{ColorChoice, Parser, Subcommand, ValueEnum};
66
use config_file::ConfigFile;
7+
use console::{self, Term};
8+
use indicatif::{ProgressBar, ProgressStyle};
79
use std::env::current_dir;
810
use std::fs::{create_dir, read_dir, read_to_string, remove_dir, write};
911
use std::io::{stdin, Read, Result};
1012
use std::panic;
1113
use std::path::{Path, PathBuf};
14+
use std::time::Duration;
1215

1316
#[derive(Parser)]
1417
#[command(name = "gpm")]
@@ -25,6 +28,7 @@ struct Args {
2528
)]
2629
/// Specify the location of the package configuration file (https://github.com/godot-package-manager#godotpackage). If -, read from stdin.
2730
config_file: PathBuf,
31+
2832
#[arg(
2933
short = 'l',
3034
long = "lock-file",
@@ -33,13 +37,21 @@ struct Args {
3337
)]
3438
/// Specify the location of the lock file. If -, print to stdout.
3539
lock_file: PathBuf,
40+
41+
#[arg(long = "colors", default_value = "auto", global = true)]
42+
/// Control color output.
43+
colors: ColorChoice,
3644
}
3745

3846
#[derive(Subcommand)]
3947
enum Actions {
4048
#[clap(short_flag = 'u')]
4149
/// Downloads the latest versions of your wanted packages.
42-
Update,
50+
Update {
51+
#[arg(short = 's')]
52+
/// To print the progress bar
53+
silent: bool,
54+
},
4355
#[clap(short_flag = 'p')]
4456
/// Deletes all installed packages.
4557
Purge,
@@ -56,44 +68,56 @@ Produces output like
5668
charset: CharSet,
5769

5870
#[arg(value_enum, default_value = "indent", long = "prefix")]
59-
/// The prefix (indentation) of how the tree entrys are displayed
71+
/// The prefix (indentation) of how the tree entrys are displayed.
6072
prefix: PrefixType,
6173

6274
#[arg(long = "tarballs", default_value = "false")]
63-
/// To print download urls next to the package name
75+
/// To print download urls next to the package name.
6476
print_tarballs: bool,
6577
},
6678
}
6779

6880
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, ValueEnum)]
69-
/// Charset for the tree subcommand
81+
/// Charset for the tree subcommand.
7082
enum CharSet {
83+
/// Unicode characters (├── └──).
7184
UTF8,
85+
/// ASCII characters (|-- `--).
7286
ASCII,
7387
}
7488

7589
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, ValueEnum)]
76-
/// Prefix type for the tree subcommand
90+
/// Prefix type for the tree subcommand.
7791
enum PrefixType {
92+
/// Indents the tree entries proportional to the depth.
7893
Indent,
94+
/// Print the depth before the entries.
7995
Depth,
96+
/// No indentation, just list.
8097
None,
8198
}
8299

83100
fn main() {
84-
#[rustfmt::skip]
85101
panic::set_hook(Box::new(|panic_info| {
86-
const RED: &str = "\x1b[1;31m";
87-
const RESET: &str = "\x1b[0m";
88102
match panic_info.location() {
89-
Some(s) => print!("{RED}err{RESET}@{}:{}:{}: ", s.file(), s.line(), s.column()),
90-
None => print!("{RED}err{RESET}: "),
103+
Some(s) => eprint!("{}@{}:{}: ", print_consts::err(), s.file(), s.line()),
104+
None => eprint!("{}: ", print_consts::err()),
91105
}
92-
if let Some(s) = panic_info.payload().downcast_ref::<&str>() { println!("{s}"); }
93-
else if let Some(s) = panic_info.payload().downcast_ref::<String>() { println!("{s}"); }
94-
else { println!("unknown"); };
106+
#[rustfmt::skip]
107+
if let Some(s) = panic_info.payload().downcast_ref::<&str>() { eprintln!("{s}"); }
108+
else if let Some(s) = panic_info.payload().downcast_ref::<String>() { eprintln!("{s}"); }
109+
else { eprintln!("unknown"); };
95110
}));
96111
let args = Args::parse();
112+
fn set_colors(val: bool) {
113+
console::set_colors_enabled(val);
114+
console::set_colors_enabled_stderr(val)
115+
}
116+
match args.colors {
117+
ColorChoice::Always => set_colors(true),
118+
ColorChoice::Never => set_colors(false),
119+
ColorChoice::Auto => set_colors(Term::stdout().is_term() && Term::stderr().is_term()),
120+
}
97121
let mut contents = String::from("");
98122
if args.config_file == Path::new("-") {
99123
let bytes = stdin()
@@ -107,7 +131,7 @@ fn main() {
107131
};
108132
let mut cfg_file = ConfigFile::new(&contents);
109133
match args.action {
110-
Actions::Update => update(&mut cfg_file, true),
134+
Actions::Update { silent } => update(&mut cfg_file, true, silent),
111135
Actions::Purge => purge(&mut cfg_file),
112136
Actions::Tree {
113137
charset,
@@ -123,23 +147,47 @@ fn main() {
123147
}
124148
}
125149

126-
fn update(cfg: &mut ConfigFile, modify: bool) {
150+
fn update(cfg: &mut ConfigFile, modify: bool, silent: bool) {
127151
if !Path::new("./addons/").exists() {
128152
create_dir("./addons/").expect("Should be able to create addons folder");
129153
}
130-
if cfg.packages.is_empty() {
154+
let packages = cfg.collect();
155+
if packages.is_empty() {
131156
panic!("No packages to update (modify the \"godot.package\" file to add packages)");
132157
}
133158
println!(
134-
"Update {} package{}",
135-
cfg.packages.len(),
136-
if cfg.packages.len() > 1 { "s" } else { "" }
159+
"Updating {} package{}",
160+
packages.len(),
161+
if packages.len() > 1 { "s" } else { "" }
137162
);
138-
cfg.for_each(|p| {
163+
let bar = if silent {
164+
ProgressBar::hidden()
165+
} else {
166+
let bar = ProgressBar::new(packages.len() as u64 * 3);
167+
bar.set_style(
168+
ProgressStyle::with_template(
169+
"[{elapsed}] {bar:20.green/red} {human_pos:>3}/{human_len:3} {msg}",
170+
)
171+
.unwrap()
172+
.progress_chars("-|-"),
173+
);
174+
bar.enable_steady_tick(Duration::new(0, 500));
175+
bar
176+
};
177+
packages.into_iter().for_each(|mut p| {
178+
bar.set_message(format!("downloading {p}"));
139179
p.download();
180+
bar.inc(1);
181+
bar.set_message(format!("modifying {p}"));
140182
if modify {
141-
p.modify()
183+
if let Err(e) = p.modify() {
184+
eprintln!(
185+
"{}: modification of {p} failed with err {e}",
186+
print_consts::warn()
187+
)
188+
}
142189
}
190+
bar.inc(1);
143191
});
144192
}
145193

@@ -180,7 +228,7 @@ fn purge(cfg: &mut ConfigFile) {
180228
};
181229
};
182230
println!(
183-
"Purge {} package{}",
231+
"Purging {} package{}",
184232
packages.len(),
185233
if packages.len() > 1 { "s" } else { "" }
186234
);
@@ -318,7 +366,7 @@ mod test_utils {
318366
fn gpm() {
319367
let _t = test_utils::mktemp();
320368
let cfg_file = &mut config_file::ConfigFile::new(&r#"packages: {"@bendn/test":2.0.10}"#.into());
321-
update(cfg_file, false);
369+
update(cfg_file, false, false);
322370
assert_eq!(test_utils::hashd("addons").join("|"), "1c2fd93634817a9e5f3f22427bb6b487520d48cf3cbf33e93614b055bcbd1329|8e77e3adf577d32c8bc98981f05d40b2eb303271da08bfa7e205d3f27e188bd7|a625595a71b159e33b3d1ee6c13bea9fc4372be426dd067186fe2e614ce76e3c|c5566e4fbea9cc6dbebd9366b09e523b20870b1d69dc812249fccd766ebce48e|c5566e4fbea9cc6dbebd9366b09e523b20870b1d69dc812249fccd766ebce48e|c850a9300388d6da1566c12a389927c3353bf931c4d6ea59b02beb302aac03ea|d060936e5f1e8b1f705066ade6d8c6de90435a91c51f122905a322251a181a5c|d711b57105906669572a0e53b8b726619e3a21463638aeda54e586a320ed0fc5|d794f3cee783779f50f37a53e1d46d9ebbc5ee7b37c36d7b6ee717773b6955cd|e4f9df20b366a114759282209ff14560401e316b0059c1746c979f478e363e87");
323371
purge(cfg_file);
324372
assert_eq!(test_utils::hashd("addons"), vec![] as Vec<String>);
@@ -336,3 +384,17 @@ fn gpm() {
336384
"└── @bendn/[email protected]\n └── @bendn/[email protected]"
337385
);
338386
}
387+
388+
pub mod print_consts {
389+
use console::{style, StyledObject};
390+
391+
#[inline]
392+
pub fn err() -> StyledObject<&'static str> {
393+
style("err").red().bold()
394+
}
395+
396+
#[inline]
397+
pub fn warn() -> StyledObject<&'static str> {
398+
style("err").yellow().bold()
399+
}
400+
}

src/package.rs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,6 @@ impl Package {
6969
/// Installs this [Package] to a download directory,
7070
/// depending on wether this package is a direct dependency or not.
7171
pub fn download(&mut self) {
72-
println!("Downloading {self}");
7372
self.purge();
7473
let resp = ureq::get(&self.get_tarball().expect("Should be able to get tarball"))
7574
.call()
@@ -338,7 +337,10 @@ impl Package {
338337
return wanted_f;
339338
}
340339
};
341-
eprintln!("Could not find path for {path:#?}");
340+
eprintln!(
341+
"{}: Could not find path for {path:#?}",
342+
crate::print_consts::warn()
343+
);
342344
return path.to_path_buf();
343345
}
344346

@@ -400,18 +402,16 @@ impl Package {
400402
}
401403

402404
/// The catalyst for `recursive_modify`.
403-
pub fn modify(&self) {
405+
pub fn modify(&self) -> io::Result<()> {
404406
if self.is_installed() == false {
405407
panic!("Attempting to modify a package that is not installed");
406408
}
407409

408-
if let Err(e) = self.recursive_modify(
410+
self.recursive_modify(
409411
Path::new(&self.download_dir()).to_path_buf(),
410412
&self.dependencies,
411413
&self.dep_map(),
412-
) {
413-
println!("Modification of {self} yielded error {e}");
414-
}
414+
)
415415
}
416416
}
417417

0 commit comments

Comments
 (0)