Extract duplicate clap helpers into clap-utils (#6812)

This commit is contained in:
Ryo Onodera
2019-11-12 09:42:08 +09:00
committed by GitHub
parent 95d6586dd7
commit d84f367317
21 changed files with 89 additions and 95 deletions

18
clap-utils/Cargo.toml Normal file
View File

@ -0,0 +1,18 @@
[package]
name = "solana-clap-utils"
version = "0.21.0"
description = "Solana utilities for the clap"
authors = ["Solana Maintainers <maintainers@solana.com>"]
repository = "https://github.com/solana-labs/solana"
license = "Apache-2.0"
homepage = "https://solana.com/"
edition = "2018"
[dependencies]
clap = "2.33.0"
semver = "0.9.0"
solana-sdk = { path = "../sdk", version = "0.21.0" }
url = "2.1.0"
[lib]
name = "solana_clap_utils"

View File

@ -0,0 +1,169 @@
use clap::ArgMatches;
use solana_sdk::{
native_token::sol_to_lamports,
pubkey::Pubkey,
signature::{read_keypair_file, Keypair, KeypairUtil},
};
// Return parsed values from matches at `name`
pub fn values_of<T>(matches: &ArgMatches<'_>, name: &str) -> Option<Vec<T>>
where
T: std::str::FromStr,
<T as std::str::FromStr>::Err: std::fmt::Debug,
{
matches
.values_of(name)
.map(|xs| xs.map(|x| x.parse::<T>().unwrap()).collect())
}
// Return a parsed value from matches at `name`
pub fn value_of<T>(matches: &ArgMatches<'_>, name: &str) -> Option<T>
where
T: std::str::FromStr,
<T as std::str::FromStr>::Err: std::fmt::Debug,
{
if let Some(value) = matches.value_of(name) {
value.parse::<T>().ok()
} else {
None
}
}
// Return the keypair for an argument with filename `name` or None if not present.
pub fn keypair_of(matches: &ArgMatches<'_>, name: &str) -> Option<Keypair> {
if let Some(value) = matches.value_of(name) {
read_keypair_file(value).ok()
} else {
None
}
}
// Return a pubkey for an argument that can itself be parsed into a pubkey,
// or is a filename that can be read as a keypair
pub fn pubkey_of(matches: &ArgMatches<'_>, name: &str) -> Option<Pubkey> {
value_of(matches, name).or_else(|| keypair_of(matches, name).map(|keypair| keypair.pubkey()))
}
pub fn amount_of(matches: &ArgMatches<'_>, name: &str, unit: &str) -> Option<u64> {
if matches.value_of(unit) == Some("lamports") {
value_of(matches, name)
} else {
value_of(matches, name).map(sol_to_lamports)
}
}
#[cfg(test)]
mod tests {
use super::*;
use clap::{App, Arg};
use solana_sdk::signature::write_keypair_file;
use std::fs;
fn app<'ab, 'v>() -> App<'ab, 'v> {
App::new("test")
.arg(
Arg::with_name("multiple")
.long("multiple")
.takes_value(true)
.multiple(true),
)
.arg(Arg::with_name("single").takes_value(true).long("single"))
}
fn tmp_file_path(name: &str, pubkey: &Pubkey) -> String {
use std::env;
let out_dir = env::var("FARF_DIR").unwrap_or_else(|_| "farf".to_string());
format!("{}/tmp/{}-{}", out_dir, name, pubkey.to_string())
}
#[test]
fn test_values_of() {
let matches =
app()
.clone()
.get_matches_from(vec!["test", "--multiple", "50", "--multiple", "39"]);
assert_eq!(values_of(&matches, "multiple"), Some(vec![50, 39]));
assert_eq!(values_of::<u64>(&matches, "single"), None);
let pubkey0 = Pubkey::new_rand();
let pubkey1 = Pubkey::new_rand();
let matches = app().clone().get_matches_from(vec![
"test",
"--multiple",
&pubkey0.to_string(),
"--multiple",
&pubkey1.to_string(),
]);
assert_eq!(
values_of(&matches, "multiple"),
Some(vec![pubkey0, pubkey1])
);
}
#[test]
fn test_value_of() {
let matches = app()
.clone()
.get_matches_from(vec!["test", "--single", "50"]);
assert_eq!(value_of(&matches, "single"), Some(50));
assert_eq!(value_of::<u64>(&matches, "multiple"), None);
let pubkey = Pubkey::new_rand();
let matches = app()
.clone()
.get_matches_from(vec!["test", "--single", &pubkey.to_string()]);
assert_eq!(value_of(&matches, "single"), Some(pubkey));
}
#[test]
fn test_keypair_of() {
let keypair = Keypair::new();
let outfile = tmp_file_path("test_gen_keypair_file.json", &keypair.pubkey());
let _ = write_keypair_file(&keypair, &outfile).unwrap();
let matches = app()
.clone()
.get_matches_from(vec!["test", "--single", &outfile]);
assert_eq!(
keypair_of(&matches, "single").unwrap().pubkey(),
keypair.pubkey()
);
assert!(keypair_of(&matches, "multiple").is_none());
let matches =
app()
.clone()
.get_matches_from(vec!["test", "--single", "random_keypair_file.json"]);
assert!(keypair_of(&matches, "single").is_none());
fs::remove_file(&outfile).unwrap();
}
#[test]
fn test_pubkey_of() {
let keypair = Keypair::new();
let outfile = tmp_file_path("test_gen_keypair_file.json", &keypair.pubkey());
let _ = write_keypair_file(&keypair, &outfile).unwrap();
let matches = app()
.clone()
.get_matches_from(vec!["test", "--single", &outfile]);
assert_eq!(pubkey_of(&matches, "single"), Some(keypair.pubkey()));
assert_eq!(pubkey_of(&matches, "multiple"), None);
let matches =
app()
.clone()
.get_matches_from(vec!["test", "--single", &keypair.pubkey().to_string()]);
assert_eq!(pubkey_of(&matches, "single"), Some(keypair.pubkey()));
let matches =
app()
.clone()
.get_matches_from(vec!["test", "--single", "random_keypair_file.json"]);
assert_eq!(pubkey_of(&matches, "single"), None);
fs::remove_file(&outfile).unwrap();
}
}

View File

@ -0,0 +1,50 @@
use solana_sdk::pubkey::Pubkey;
use solana_sdk::signature::read_keypair_file;
// Return an error if a pubkey cannot be parsed.
pub fn is_pubkey(string: String) -> Result<(), String> {
match string.parse::<Pubkey>() {
Ok(_) => Ok(()),
Err(err) => Err(format!("{:?}", err)),
}
}
// Return an error if a keypair file cannot be parsed.
pub fn is_keypair(string: String) -> Result<(), String> {
read_keypair_file(&string)
.map(|_| ())
.map_err(|err| format!("{:?}", err))
}
// Return an error if string cannot be parsed as pubkey string or keypair file location
pub fn is_pubkey_or_keypair(string: String) -> Result<(), String> {
is_pubkey(string.clone()).or_else(|_| is_keypair(string))
}
// Return an error if a url cannot be parsed.
pub fn is_url(string: String) -> Result<(), String> {
match url::Url::parse(&string) {
Ok(url) => {
if url.has_host() {
Ok(())
} else {
Err("no host provided".to_string())
}
}
Err(err) => Err(format!("{:?}", err)),
}
}
pub fn is_semver(semver: &str) -> Result<(), String> {
match semver::Version::parse(&semver) {
Ok(_) => Ok(()),
Err(err) => Err(format!("{:?}", err)),
}
}
pub fn is_release_channel(channel: &str) -> Result<(), String> {
match channel {
"edge" | "beta" | "stable" => Ok(()),
_ => Err(format!("Invalid release channel {}", channel)),
}
}

2
clap-utils/src/lib.rs Normal file
View File

@ -0,0 +1,2 @@
pub mod input_parsers;
pub mod input_validators;