mirror of
https://github.com/supleed2/omg-rs.git
synced 2024-12-22 05:35:52 +00:00
Add API key prompt, switch
command, update config format
Add handling and printing of `AddressResponse` to `stdout`
This commit is contained in:
parent
d0345f0381
commit
275ca23c12
|
@ -12,11 +12,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||||
- Changelog file (this file) and reference in the [readme](README.md)
|
- Changelog file (this file) and reference in the [readme](README.md)
|
||||||
- Automated release with pre-built binaries using GitHub Actions: [release.yaml](.github/workflows/release.yaml)
|
- Automated release with pre-built binaries using GitHub Actions: [release.yaml](.github/workflows/release.yaml)
|
||||||
- Prami logo, taken from [here](https://sarajoy.dev/blog/short/2023-01-18-ascii-art-heart/) with permission
|
- Prami logo, taken from [here](https://sarajoy.dev/blog/short/2023-01-18-ascii-art-heart/) with permission
|
||||||
|
- Prompt for API key so it isn't recorded in shell history
|
||||||
|
- `switch` subcommand for selecting `default_name` and `api_key` to load
|
||||||
|
- Handling of `AddressResponse` and printing to `stdout`
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
- Switch to external version of commands crate, [omg-api](https://github.com/supleed2/omg-api)
|
- Switch to external version of commands crate, [omg-api](https://github.com/supleed2/omg-api)
|
||||||
- Update [omg-api](https://github.com/supleed2/omg-api) to v0.2.0
|
- Update [omg-api](https://github.com/supleed2/omg-api) to v0.2.0
|
||||||
|
- Update config format and handling in `main.rs`
|
||||||
|
|
||||||
### Deprecated
|
### Deprecated
|
||||||
|
|
||||||
|
|
147
src/main.rs
147
src/main.rs
|
@ -1,52 +1,128 @@
|
||||||
use anyhow::Context;
|
use anyhow::Context;
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use directories::ProjectDirs;
|
use directories::ProjectDirs;
|
||||||
use omg_api::Commands;
|
use omg_api::*;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
use std::collections::HashMap;
|
||||||
use std::fs::read_to_string;
|
use std::fs::read_to_string;
|
||||||
|
|
||||||
mod cli;
|
mod cli;
|
||||||
use cli::Cli;
|
use cli::Cli;
|
||||||
|
|
||||||
#[derive(Default, Deserialize, Serialize)]
|
#[derive(Deserialize, Serialize)]
|
||||||
struct Config {
|
struct Config {
|
||||||
api_key: Option<String>,
|
default_name: Option<String>,
|
||||||
name: Option<String>,
|
names: HashMap<String, String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() -> anyhow::Result<()> {
|
fn main() -> anyhow::Result<()> {
|
||||||
let cli = Cli::parse();
|
let cli = Cli::parse();
|
||||||
|
|
||||||
if let Some(Commands::Auth { api_key }) = cli.command {
|
if let Commands::Auth { name } = cli.command {
|
||||||
match save_api_key(&api_key) {
|
let api_key = dialoguer::Password::new()
|
||||||
Ok(_) => std::process::exit(0),
|
.with_prompt("API Key")
|
||||||
Err(_) => std::process::exit(1),
|
.interact()?;
|
||||||
};
|
save_api_key(&api_key, &name)?;
|
||||||
|
println!("API key for address \"{name}\" saved to config.toml");
|
||||||
|
std::process::exit(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
let config = ProjectDirs::from("com", "supleed2", "omg")
|
let config = ProjectDirs::from("com", "supleed2", "omg")
|
||||||
.and_then(|dirs| read_to_string(dirs.config_dir().join("config.toml")).ok())
|
.and_then(|dirs| read_to_string(dirs.config_dir().join("config.toml")).ok())
|
||||||
.and_then(|str| toml::from_str::<Config>(&str).ok())
|
.and_then(|str| toml::from_str::<Config>(&str).ok())
|
||||||
.context("Unable to parse config.json as config struct.")?;
|
.context("Unable to parse config.toml as config struct.")?;
|
||||||
|
|
||||||
|
if let Commands::Switch { address } = cli.command {
|
||||||
|
match address {
|
||||||
|
Some(new) => {
|
||||||
|
set_default_name(config, &new)?;
|
||||||
|
println!("Default address in Config.toml updated to \"{new}\"")
|
||||||
|
}
|
||||||
|
None => list_saved_addresses(config)?,
|
||||||
|
}
|
||||||
|
std::process::exit(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
let address = std::env::var("OMGLOL_USERNAME")
|
||||||
|
.ok()
|
||||||
|
.or(config.default_name)
|
||||||
|
.expect("omg.lol address not provided as either environment variable (`OMGLOL_USERNAME`) or in config file as default_name");
|
||||||
|
|
||||||
let api_key = std::env::var("OMGLOL_API_KEY")
|
let api_key = std::env::var("OMGLOL_API_KEY")
|
||||||
.ok()
|
.ok()
|
||||||
.or(config.api_key)
|
.or_else(|| config.names.get(&address).cloned())
|
||||||
.expect("omg.lol API key not provided as either environment variable or in config file");
|
.expect("omg.lol API key not provided as either environment variable (`OMGLOL_API_KEY`) or in config file");
|
||||||
|
|
||||||
let name = cli.name
|
let resp = cli.command.process(&address, &api_key)?;
|
||||||
.or(std::env::var("OMGLOL_USERNAME").ok())
|
|
||||||
.or(config.name)
|
|
||||||
.expect("omg.lol username not provided as command line option, environment variable or in config file");
|
|
||||||
|
|
||||||
println!("omg-rs, ready for @{name}");
|
|
||||||
if cli.verbose > 0 {
|
if cli.verbose > 0 {
|
||||||
println!("API key: {}", api_key);
|
println!("\nAPI Response: {resp:?}");
|
||||||
}
|
}
|
||||||
Ok(())
|
|
||||||
|
match resp {
|
||||||
|
CommandResponse::Todo(_) => println!(
|
||||||
|
"This command has not been implemented yet, please look forward to future releases!"
|
||||||
|
),
|
||||||
|
CommandResponse::Address(r) => match r {
|
||||||
|
AddressResponse::IsAvailable(IsAvailable { response: r }) => {
|
||||||
|
println!(
|
||||||
|
"Address \"{}\" is {}available",
|
||||||
|
r.address,
|
||||||
|
if r.available { "" } else { "NOT " }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
AddressResponse::GetExpiry(GetExpiry { response: r }) => println!("{}", r.message),
|
||||||
|
AddressResponse::GetPublicInfo(GetPublicInfo { response: r }) => {
|
||||||
|
println!(
|
||||||
|
"Address \"{}\":\n{}\nExpired: {}\nVerified: {}",
|
||||||
|
r.address, r.message, r.expiration.expired, r.verification.verified
|
||||||
|
)
|
||||||
|
}
|
||||||
|
AddressResponse::GetInfo(GetInfo { response: r }) => {
|
||||||
|
println!(
|
||||||
|
"Address \"{}\" (owned by \"{}\"):\n{}\nExpired: {}\nVerified: {}",
|
||||||
|
r.address, r.owner, r.message, r.expiration.expired, r.verification.verified
|
||||||
|
)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
anyhow::Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn save_api_key(api_key: &str) -> std::io::Result<()> {
|
fn list_saved_addresses(config: Config) -> anyhow::Result<()> {
|
||||||
|
println!("Saved addresses:");
|
||||||
|
for name in config.names.keys() {
|
||||||
|
println!("{name}");
|
||||||
|
}
|
||||||
|
let exe = std::env::current_exe()?;
|
||||||
|
let exe = exe.file_name().and_then(|e| e.to_str()).unwrap_or("omg");
|
||||||
|
if let Some(name) = config.default_name {
|
||||||
|
println!("Current default: {name}, use `{exe} switch [name]` to change");
|
||||||
|
} else {
|
||||||
|
println!("Current default unset, use `{exe} switch [name]` to set");
|
||||||
|
}
|
||||||
|
anyhow::Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_default_name(config: Config, new: &str) -> std::io::Result<()> {
|
||||||
|
if config.names.contains_key(new) {
|
||||||
|
let config_path = ProjectDirs::from("com", "supleed2", "omg")
|
||||||
|
.expect("Unable to access app config directory (while setting default address).")
|
||||||
|
.config_dir()
|
||||||
|
.join("config.toml");
|
||||||
|
let toml_str = toml::to_string_pretty(&Config {
|
||||||
|
default_name: Some(new.to_string()),
|
||||||
|
names: config.names,
|
||||||
|
})
|
||||||
|
.expect("Unable to convert updated config to TOML (while setting default address).");
|
||||||
|
std::fs::write(config_path, toml_str)
|
||||||
|
} else {
|
||||||
|
eprintln!("Address \"{new}\" not in config.toml saved addresses");
|
||||||
|
std::process::exit(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn save_api_key(api_key: &str, name: &str) -> std::io::Result<()> {
|
||||||
let config_path = ProjectDirs::from("com", "supleed2", "omg")
|
let config_path = ProjectDirs::from("com", "supleed2", "omg")
|
||||||
.expect("Unable to access app config directory (while saving API key).")
|
.expect("Unable to access app config directory (while saving API key).")
|
||||||
.config_dir()
|
.config_dir()
|
||||||
|
@ -56,17 +132,32 @@ fn save_api_key(api_key: &str) -> std::io::Result<()> {
|
||||||
.parent()
|
.parent()
|
||||||
.expect("Unable to get parent dir of config.toml"),
|
.expect("Unable to get parent dir of config.toml"),
|
||||||
);
|
);
|
||||||
let Config { api_key: _, name } = read_to_string(&config_path)
|
let Config {
|
||||||
|
default_name,
|
||||||
|
mut names,
|
||||||
|
} = read_to_string(&config_path)
|
||||||
.ok()
|
.ok()
|
||||||
.and_then(|str| toml::from_str::<Config>(&str).ok())
|
.and_then(|str| toml::from_str::<Config>(&str).ok())
|
||||||
.unwrap_or_default();
|
.unwrap_or_else(|| {
|
||||||
|
eprintln!("Failed to load config, recreating... (Old config will be overwritten)");
|
||||||
|
Config {
|
||||||
|
default_name: Some(name.to_string()),
|
||||||
|
names: HashMap::new(),
|
||||||
|
}
|
||||||
|
});
|
||||||
|
let default_name = default_name.or_else(|| {
|
||||||
|
eprintln!("No default_name in config, setting to \"{name}\"");
|
||||||
|
Some(name.to_string())
|
||||||
|
});
|
||||||
|
names.insert(name.to_string(), api_key.to_string());
|
||||||
let toml_str = toml::to_string_pretty(&Config {
|
let toml_str = toml::to_string_pretty(&Config {
|
||||||
api_key: Some(api_key.to_string()),
|
default_name,
|
||||||
name,
|
names,
|
||||||
})
|
})
|
||||||
.expect("Unable to convert updated config to TOML (when trying to save API key).");
|
.expect("Unable to convert updated config to TOML (while saving API key).");
|
||||||
std::fs::write(&config_path, toml_str)
|
std::fs::write(&config_path, toml_str)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Tutorial at: https://docs.rs/clap/latest/clap/_derive/_tutorial/index.html
|
// Clap arg relations: https://docs.rs/clap/latest/clap/_derive/_tutorial/index.html#argument-relations
|
||||||
// More info at: https://docs.rs/clap/latest/clap/_derive/index.html
|
// Clap derive docs: https://docs.rs/clap/latest/clap/_derive/index.html
|
||||||
|
// Thiserror docs: https://docs.rs/thiserror/latest/thiserror/index.html
|
||||||
|
|
Loading…
Reference in a new issue