Skip to content

Commit

Permalink
support color disable
Browse files Browse the repository at this point in the history
  • Loading branch information
spencerwooo committed Nov 27, 2023
1 parent 9704213 commit 6051faa
Show file tree
Hide file tree
Showing 6 changed files with 142 additions and 52 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ base64 = "0.21"
sha1 = "0.10"
clap = { version = "4.4", features = ["derive"] }
directories = "5.0"
owo-colors = "3"
owo-colors = { version = "3", features = ["supports-colors"] }
rpassword = "7.3"
rprompt = "2.1"
tabled = { version = "0.14", features = ["color"] }
Expand Down
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,9 @@ Options:
-V, --version Print version
```

> [!TIP]
> Use environment variable `NO_COLOR=true` to disable colored output.
## Credentials

User credentials are stored in config file `bit-user.json`. Available config file paths can be listed with:
Expand Down
55 changes: 38 additions & 17 deletions src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ use hmac::Mac;
use md5::Digest;
use md5::Md5;
use owo_colors::OwoColorize;
use owo_colors::Stream::Stdout;
use reqwest::Client;

use serde::Deserialize;
Expand Down Expand Up @@ -121,19 +122,27 @@ pub async fn get_login_state(client: &Client) -> Result<SrunLoginState> {

/// Get the ac_id of the current device
async fn get_acid(client: &Client) -> Result<String> {
let resp = client
.get(SRUN_PORTAL)
.send()
.await
.with_context(|| format!("failed to get ac_id from `{}`", SRUN_PORTAL.underline()))?;
let resp = client.get(SRUN_PORTAL).send().await.with_context(|| {
format!(
"failed to get ac_id from `{}`",
SRUN_PORTAL.if_supports_color(Stdout, |t| t.underline())
)
})?;
let redirect_url = resp.url().to_string();
let parsed_url = url::Url::parse(&redirect_url)
.with_context(|| format!("failed to parse url `{}`", redirect_url.underline()))?;
let parsed_url = url::Url::parse(&redirect_url).with_context(|| {
format!(
"failed to parse url `{}`",
redirect_url.if_supports_color(Stdout, |t| t.underline())
)
})?;

let mut query = parsed_url.query_pairs().into_owned();
let ac_id = query
.find(|(key, _)| key == "ac_id")
.with_context(|| format!("failed to get ac_id from `{}`", redirect_url.underline()))?;
let ac_id = query.find(|(key, _)| key == "ac_id").with_context(|| {
format!(
"failed to get ac_id from `{}`",
redirect_url.if_supports_color(Stdout, |t| t.underline())
)
})?;
Ok(ac_id.1)
}

Expand Down Expand Up @@ -215,7 +224,10 @@ impl SrunClient {
if self.login_state.error == "ok" {
bail!(
"{} already logged in",
self.login_state.online_ip.to_string().underline()
self.login_state
.online_ip
.to_string()
.if_supports_color(Stdout, |t| t.underline())
)
}

Expand Down Expand Up @@ -286,23 +298,28 @@ impl SrunClient {
pub async fn logout(&self) -> Result<SrunPortalResponse> {
// check if already logged out
if self.login_state.error == "not_online_error" {
bail!("{} already logged out", self.ip.to_string().underline())
bail!(
"{} already logged out",
self.ip
.to_string()
.if_supports_color(Stdout, |t| t.underline())
)
}

// check if username match
let logged_in_username = self.login_state.user_name.clone().unwrap_or_default();
if logged_in_username != self.username {
println!(
"{} logged in user {} does not match yourself {}",
"warning:".yellow(),
"warning:".if_supports_color(Stdout, |t| t.yellow()),
format!("({})", logged_in_username).dimmed(),
format!("({})", self.username).dimmed()
);

// tip to provide user override
println!(
"{:>8} provide username argument {} to override and logout current session",
"tip:".cyan(),
"tip:".if_supports_color(Stdout, |t| t.cyan()),
format!("`--user {}`", logged_in_username)
.bold()
.bright_green()
Expand All @@ -314,9 +331,13 @@ impl SrunClient {
if logged_in_ip != self.ip {
println!(
"{} logged in ip (`{}`) does not match `{}`",
"warning:".yellow(),
logged_in_ip.to_string().underline(),
self.ip.to_string().underline()
"warning:".if_supports_color(Stdout, |t| t.yellow()),
logged_in_ip
.to_string()
.if_supports_color(Stdout, |t| t.underline()),
self.ip
.to_string()
.if_supports_color(Stdout, |t| t.underline())
);
}

Expand Down
61 changes: 44 additions & 17 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ use cli::Commands;
use client::get_login_state;
use client::SrunClient;
use owo_colors::OwoColorize;
use owo_colors::Stream::Stderr;
use owo_colors::Stream::Stdout;

use clap::Parser;
use tables::print_config_paths;
Expand All @@ -19,7 +21,12 @@ use tables::print_login_state;
#[tokio::main]
async fn main() {
if let Err(err) = cli().await {
eprintln!("{} {} {}", "bitsrun:".bright_red(), "[error]".dimmed(), err);
eprintln!(
"{} {} {}",
"bitsrun:".if_supports_color(Stderr, |t| t.bright_red()),
"[error]".if_supports_color(Stderr, |t| t.dimmed()),
err
);
std::process::exit(1);
}
}
Expand Down Expand Up @@ -47,18 +54,25 @@ async fn cli() -> Result<()> {
if login_state.error == "ok" {
println!(
"{} {} {} is online",
"bitsrun:".bright_green(),
&login_state.online_ip.to_string().underline(),
format!("({})", login_state.user_name.clone().unwrap_or_default()).dimmed()
"bitsrun:".if_supports_color(Stdout, |t| t.bright_green()),
&login_state
.online_ip
.to_string()
.if_supports_color(Stdout, |t| t.underline()),
format!("({})", login_state.user_name.clone().unwrap_or_default())
.if_supports_color(Stdout, |t| t.dimmed())
);

// print status table
print_login_state(login_state);
} else {
println!(
"{} {} is offline",
"bitsrun:".blue(),
login_state.online_ip.to_string().underline()
"bitsrun:".if_supports_color(Stdout, |t| t.blue()),
login_state
.online_ip
.to_string()
.if_supports_color(Stdout, |t| t.underline())
);
}
}
Expand Down Expand Up @@ -86,41 +100,54 @@ async fn cli() -> Result<()> {
match resp.error.as_str() {
"ok" => println!(
"{} {} {} logged in",
"bitsrun:".bright_green(),
resp.online_ip.to_string().underline(),
format!("({})", resp.username.clone().unwrap_or_default()).dimmed()
"bitsrun:".if_supports_color(Stdout, |t| t.bright_green()),
resp.online_ip
.to_string()
.if_supports_color(Stdout, |t| t.underline()),
format!("({})", resp.username.clone().unwrap_or_default())
.if_supports_color(Stdout, |t| t.dimmed())
),
_ => println!(
"{} failed to login, {} {}",
"bitsrun:".red(),
"bitsrun:".if_supports_color(Stdout, |t| t.red()),
resp.error,
format!("({})", resp.error_msg).dimmed()
format!("({})", resp.error_msg).if_supports_color(Stdout, |t| t.dimmed())
),
}

if args.verbose {
let pretty_json = serde_json::to_string_pretty(&resp)?;
println!("{} response from API\n{}", "bitsrun:".blue(), pretty_json);
println!(
"{} response from API\n{}",
"bitsrun:".if_supports_color(Stdout, |t| t.blue()),
pretty_json
);
}
} else if matches!(args.command, Some(Commands::Logout(_))) {
let resp = srun_client.logout().await?;
match resp.error.as_str() {
"ok" => println!(
"{} {} logged out",
"bitsrun:".green(),
resp.online_ip.to_string().underline()
"bitsrun:".if_supports_color(Stdout, |t| t.green()),
resp.online_ip
.to_string()
.if_supports_color(Stdout, |t| t.underline())
),
_ => println!(
"{} failed to logout, {} {}",
"bitsrun:".red(),
"bitsrun:".if_supports_color(Stdout, |t| t.red()),
resp.error,
format!("({})", resp.error_msg).dimmed()
format!("({})", resp.error_msg).if_supports_color(Stdout, |t| t.dimmed())
),
}

if args.verbose {
let pretty_json = serde_json::to_string_pretty(&resp)?;
println!("{} response from API\n{}", "bitsrun:".blue(), pretty_json);
println!(
"{} response from API\n{}",
"bitsrun:".if_supports_color(Stdout, |t| t.blue()),
pretty_json
);
}
}
}
Expand Down
31 changes: 26 additions & 5 deletions src/tables.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use chrono_humanize::Tense::Present;
use humansize::format_size;
use humansize::BINARY;
use owo_colors::OwoColorize;
use owo_colors::Stream::Stdout;
use tabled::builder::Builder;
use tabled::settings::Style;
use tabled::settings::Width;
Expand All @@ -21,7 +22,10 @@ use tabled::settings::Width;
/// │ 1 │ C:\Users\{USERNAME}\AppData\Roaming\bitsrun\bit-user.json │
/// └──────────┴───────────────────────────────────────────────────────────┘
pub fn print_config_paths() {
println!("{} list of possible config paths", "bitsrun:".blue());
println!(
"{} list of possible config paths",
"bitsrun:".if_supports_color(Stdout, |t| t.blue())
);

let mut builder = Builder::default();
builder.set_header(["Priority", "Possible Config Path"]);
Expand All @@ -35,6 +39,14 @@ pub fn print_config_paths() {
}

/// Print login state table
///
/// # Example output
///
/// ┌────────────────┬───────────────┬───────────────┬─────────┐
/// │ Traffic Used │ Online Time │ User Balance │ Wallet │
/// ├────────────────┼───────────────┼───────────────┼─────────┤
/// │ 188.10 GiB │ 2 months │ 10.00 │ 0.00 │
/// └────────────────┴───────────────┴───────────────┴─────────┘
pub fn print_login_state(state: SrunLoginState) {
let mut builder = Builder::default();
builder.set_header(["Traffic Used", "Online Time", "User Balance", "Wallet"]);
Expand All @@ -49,10 +61,19 @@ pub fn print_login_state(state: SrunLoginState) {
let wallet = state.wallet_balance.unwrap_or(0) as f32;

builder.push_record([
format_size(traffic_used, BINARY).green().to_string(),
human_time.to_text_en(Rough, Present).yellow().to_string(),
format!("{:.2}", user_balance).cyan().to_string(),
format!("{:.2}", wallet).magenta().to_string(),
format_size(traffic_used, BINARY)
.if_supports_color(Stdout, |t| t.green())
.to_string(),
human_time
.to_text_en(Rough, Present)
.if_supports_color(Stdout, |t| t.yellow())
.to_string(),
format!("{:.2}", user_balance)
.if_supports_color(Stdout, |t| t.cyan())
.to_string(),
format!("{:.2}", wallet)
.if_supports_color(Stdout, |t| t.magenta())
.to_string(),
]);

let mut table = builder.build();
Expand Down
42 changes: 30 additions & 12 deletions src/user.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use anyhow::anyhow;
use anyhow::Context;
use anyhow::Result;
use owo_colors::OwoColorize;
use owo_colors::Stream::Stdout;
use serde::Deserialize;
use serde::Serialize;

Expand Down Expand Up @@ -101,14 +102,23 @@ fn parse_config_file(config_path: &Option<String>) -> Result<BitUserPartial> {
if config.is_empty() {
Err(anyhow!(
"config file `{}` not found, available paths can be found with `{}`",
"bit-user.json".underline(),
"bitsrun config-paths".cyan().bold().underline()
"bit-user.json".if_supports_color(Stdout, |t| t.underline()),
"bitsrun config-paths".if_supports_color(Stdout, |t| t.cyan())
))
} else {
let user_str_from_file = fs::read_to_string(&config)
.with_context(|| format!("failed to read config file `{}`", &config.underline()))?;
let user_str_from_file = fs::read_to_string(&config).with_context(|| {
format!(
"failed to read config file `{}`",
&config.if_supports_color(Stdout, |t| t.underline())
)
})?;
let user_from_file = serde_json::from_str::<BitUserPartial>(&user_str_from_file)
.with_context(|| format!("failed to parse config file `{}`", &config.underline()))?;
.with_context(|| {
format!(
"failed to parse config file `{}`",
&config.if_supports_color(Stdout, |t| t.underline())
)
})?;
Ok(user_from_file)
}
}
Expand All @@ -130,25 +140,33 @@ pub fn get_bit_user(
let mut user_from_file = BitUserPartial::default();
match parse_config_file(config_path) {
Ok(value) => user_from_file = value,
Err(e) => println!("{} {}", "warning:".yellow(), e),
Err(e) => println!(
"{} {}",
"warning:".if_supports_color(Stdout, |t| t.yellow()),
e
),
}

match user_from_file.username {
Some(username) => bit_user.username.get_or_insert(username),
None => bit_user.username.get_or_insert_with(|| {
rprompt::prompt_reply("-> please enter your campus id: ".dimmed())
.with_context(|| "failed to read username")
.unwrap()
rprompt::prompt_reply(
"-> please enter your campus id: ".if_supports_color(Stdout, |t| t.dimmed()),
)
.with_context(|| "failed to read username")
.unwrap()
}),
};

match user_from_file.password {
Some(password) => bit_user.password.get_or_insert(password),
None => bit_user.password.get_or_insert_with(|| {
if require_password {
rpassword::prompt_password("-> please enter your password: ".dimmed())
.with_context(|| "failed to read password")
.unwrap()
rpassword::prompt_password(
"-> please enter your password: ".if_supports_color(Stdout, |t| t.dimmed()),
)
.with_context(|| "failed to read password")
.unwrap()
} else {
"".into()
}
Expand Down

0 comments on commit 6051faa

Please sign in to comment.