2020-02-21 14:55:53 -07:00
|
|
|
use crate::keypair::{parse_keypair_path, KeypairUrl, ASK_KEYWORD};
|
2019-12-30 22:57:47 -07:00
|
|
|
use chrono::DateTime;
|
2020-02-07 11:26:56 -07:00
|
|
|
use solana_sdk::{
|
2020-12-28 11:37:47 -07:00
|
|
|
clock::{Epoch, Slot},
|
2020-02-07 11:26:56 -07:00
|
|
|
hash::Hash,
|
|
|
|
pubkey::Pubkey,
|
|
|
|
signature::{read_keypair_file, Signature},
|
|
|
|
};
|
2020-06-15 14:37:53 +01:00
|
|
|
use std::fmt::Display;
|
2019-11-25 21:09:57 -08:00
|
|
|
use std::str::FromStr;
|
2019-09-03 10:38:12 -07:00
|
|
|
|
2020-06-15 14:37:53 +01:00
|
|
|
fn is_parsable_generic<U, T>(string: T) -> Result<(), String>
|
|
|
|
where
|
|
|
|
T: AsRef<str> + Display,
|
|
|
|
U: FromStr,
|
|
|
|
U::Err: Display,
|
|
|
|
{
|
|
|
|
string
|
|
|
|
.as_ref()
|
|
|
|
.parse::<U>()
|
|
|
|
.map(|_| ())
|
|
|
|
.map_err(|err| format!("error parsing '{}': {}", string, err))
|
|
|
|
}
|
|
|
|
|
|
|
|
// Return an error if string cannot be parsed as type T.
|
|
|
|
// Takes a String to avoid second type parameter when used as a clap validator
|
|
|
|
pub fn is_parsable<T>(string: String) -> Result<(), String>
|
|
|
|
where
|
|
|
|
T: FromStr,
|
|
|
|
T::Err: Display,
|
|
|
|
{
|
|
|
|
is_parsable_generic::<T, String>(string)
|
|
|
|
}
|
|
|
|
|
2019-09-03 10:38:12 -07:00
|
|
|
// Return an error if a pubkey cannot be parsed.
|
2020-06-15 14:37:53 +01:00
|
|
|
pub fn is_pubkey<T>(string: T) -> Result<(), String>
|
|
|
|
where
|
|
|
|
T: AsRef<str> + Display,
|
|
|
|
{
|
|
|
|
is_parsable_generic::<Pubkey, _>(string)
|
2019-09-03 10:38:12 -07:00
|
|
|
}
|
|
|
|
|
2019-11-25 21:09:57 -08:00
|
|
|
// Return an error if a hash cannot be parsed.
|
2020-06-15 14:37:53 +01:00
|
|
|
pub fn is_hash<T>(string: T) -> Result<(), String>
|
|
|
|
where
|
|
|
|
T: AsRef<str> + Display,
|
|
|
|
{
|
|
|
|
is_parsable_generic::<Hash, _>(string)
|
2019-11-25 21:09:57 -08:00
|
|
|
}
|
|
|
|
|
2019-09-03 10:38:12 -07:00
|
|
|
// Return an error if a keypair file cannot be parsed.
|
2020-06-15 14:37:53 +01:00
|
|
|
pub fn is_keypair<T>(string: T) -> Result<(), String>
|
|
|
|
where
|
|
|
|
T: AsRef<str> + Display,
|
|
|
|
{
|
|
|
|
read_keypair_file(string.as_ref())
|
2019-09-03 10:38:12 -07:00
|
|
|
.map(|_| ())
|
2020-03-13 00:20:49 -06:00
|
|
|
.map_err(|err| format!("{}", err))
|
2019-09-03 10:38:12 -07:00
|
|
|
}
|
|
|
|
|
2019-11-23 11:55:43 -05:00
|
|
|
// Return an error if a keypair file cannot be parsed
|
2020-06-15 14:37:53 +01:00
|
|
|
pub fn is_keypair_or_ask_keyword<T>(string: T) -> Result<(), String>
|
|
|
|
where
|
|
|
|
T: AsRef<str> + Display,
|
|
|
|
{
|
|
|
|
if string.as_ref() == ASK_KEYWORD {
|
2019-11-23 11:55:43 -05:00
|
|
|
return Ok(());
|
|
|
|
}
|
2020-06-15 14:37:53 +01:00
|
|
|
read_keypair_file(string.as_ref())
|
2019-11-23 11:55:43 -05:00
|
|
|
.map(|_| ())
|
2020-03-13 00:20:49 -06:00
|
|
|
.map_err(|err| format!("{}", err))
|
2019-11-23 11:55:43 -05:00
|
|
|
}
|
|
|
|
|
2019-09-03 10:38:12 -07:00
|
|
|
// Return an error if string cannot be parsed as pubkey string or keypair file location
|
2020-06-15 14:37:53 +01:00
|
|
|
pub fn is_pubkey_or_keypair<T>(string: T) -> Result<(), String>
|
|
|
|
where
|
|
|
|
T: AsRef<str> + Display,
|
|
|
|
{
|
|
|
|
is_pubkey(string.as_ref()).or_else(|_| is_keypair(string))
|
2019-09-03 10:38:12 -07:00
|
|
|
}
|
|
|
|
|
2020-03-16 16:17:13 -06:00
|
|
|
// Return an error if string cannot be parsed as a pubkey string, or a valid Signer that can
|
|
|
|
// produce a pubkey()
|
2020-06-15 14:37:53 +01:00
|
|
|
pub fn is_valid_pubkey<T>(string: T) -> Result<(), String>
|
|
|
|
where
|
|
|
|
T: AsRef<str> + Display,
|
|
|
|
{
|
|
|
|
match parse_keypair_path(string.as_ref()) {
|
2020-02-21 14:55:53 -07:00
|
|
|
KeypairUrl::Filepath(path) => is_keypair(path),
|
|
|
|
_ => Ok(()),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-16 16:17:13 -06:00
|
|
|
// Return an error if string cannot be parsed as a valid Signer. This is an alias of
|
|
|
|
// `is_valid_pubkey`, and does accept pubkey strings, even though a Pubkey is not by itself
|
|
|
|
// sufficient to sign a transaction.
|
|
|
|
//
|
|
|
|
// In the current offline-signing implementation, a pubkey is the valid input for a signer field
|
|
|
|
// when paired with an offline `--signer` argument to provide a Presigner (pubkey + signature).
|
|
|
|
// Clap validators can't check multiple fields at once, so the verification that a `--signer` is
|
|
|
|
// also provided and correct happens in parsing, not in validation.
|
2020-06-15 14:37:53 +01:00
|
|
|
pub fn is_valid_signer<T>(string: T) -> Result<(), String>
|
|
|
|
where
|
|
|
|
T: AsRef<str> + Display,
|
|
|
|
{
|
2020-03-16 16:17:13 -06:00
|
|
|
is_valid_pubkey(string)
|
|
|
|
}
|
|
|
|
|
2019-11-25 21:09:57 -08:00
|
|
|
// Return an error if string cannot be parsed as pubkey=signature string
|
2020-06-15 14:37:53 +01:00
|
|
|
pub fn is_pubkey_sig<T>(string: T) -> Result<(), String>
|
|
|
|
where
|
|
|
|
T: AsRef<str> + Display,
|
|
|
|
{
|
|
|
|
let mut signer = string.as_ref().split('=');
|
2019-11-25 21:09:57 -08:00
|
|
|
match Pubkey::from_str(
|
|
|
|
signer
|
|
|
|
.next()
|
|
|
|
.ok_or_else(|| "Malformed signer string".to_string())?,
|
|
|
|
) {
|
|
|
|
Ok(_) => {
|
|
|
|
match Signature::from_str(
|
|
|
|
signer
|
|
|
|
.next()
|
|
|
|
.ok_or_else(|| "Malformed signer string".to_string())?,
|
|
|
|
) {
|
|
|
|
Ok(_) => Ok(()),
|
2020-03-13 00:20:49 -06:00
|
|
|
Err(err) => Err(format!("{}", err)),
|
2019-11-25 21:09:57 -08:00
|
|
|
}
|
|
|
|
}
|
2020-03-13 00:20:49 -06:00
|
|
|
Err(err) => Err(format!("{}", err)),
|
2019-11-25 21:09:57 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-09-03 10:38:12 -07:00
|
|
|
// Return an error if a url cannot be parsed.
|
2020-06-15 14:37:53 +01:00
|
|
|
pub fn is_url<T>(string: T) -> Result<(), String>
|
|
|
|
where
|
|
|
|
T: AsRef<str> + Display,
|
|
|
|
{
|
|
|
|
match url::Url::parse(string.as_ref()) {
|
2019-09-03 10:38:12 -07:00
|
|
|
Ok(url) => {
|
|
|
|
if url.has_host() {
|
|
|
|
Ok(())
|
|
|
|
} else {
|
|
|
|
Err("no host provided".to_string())
|
|
|
|
}
|
|
|
|
}
|
2020-03-13 00:20:49 -06:00
|
|
|
Err(err) => Err(format!("{}", err)),
|
2019-09-03 10:38:12 -07:00
|
|
|
}
|
|
|
|
}
|
2019-11-12 09:42:08 +09:00
|
|
|
|
2020-12-28 11:37:47 -07:00
|
|
|
pub fn is_epoch<T>(epoch: T) -> Result<(), String>
|
|
|
|
where
|
|
|
|
T: AsRef<str> + Display,
|
|
|
|
{
|
|
|
|
is_parsable_generic::<Epoch, _>(epoch)
|
|
|
|
}
|
|
|
|
|
2020-06-15 14:37:53 +01:00
|
|
|
pub fn is_slot<T>(slot: T) -> Result<(), String>
|
|
|
|
where
|
|
|
|
T: AsRef<str> + Display,
|
|
|
|
{
|
|
|
|
is_parsable_generic::<Slot, _>(slot)
|
2020-03-02 11:47:58 -07:00
|
|
|
}
|
|
|
|
|
2020-06-15 14:37:53 +01:00
|
|
|
pub fn is_port<T>(port: T) -> Result<(), String>
|
|
|
|
where
|
|
|
|
T: AsRef<str> + Display,
|
|
|
|
{
|
|
|
|
is_parsable_generic::<u16, _>(port)
|
2019-11-20 15:21:34 -07:00
|
|
|
}
|
2019-12-04 00:54:01 +05:30
|
|
|
|
2020-06-15 14:37:53 +01:00
|
|
|
pub fn is_valid_percentage<T>(percentage: T) -> Result<(), String>
|
|
|
|
where
|
|
|
|
T: AsRef<str> + Display,
|
|
|
|
{
|
2019-12-04 00:54:01 +05:30
|
|
|
percentage
|
2020-06-15 14:37:53 +01:00
|
|
|
.as_ref()
|
2019-12-04 00:54:01 +05:30
|
|
|
.parse::<u8>()
|
|
|
|
.map_err(|e| {
|
|
|
|
format!(
|
2020-03-13 00:20:49 -06:00
|
|
|
"Unable to parse input percentage, provided: {}, err: {}",
|
2019-12-04 00:54:01 +05:30
|
|
|
percentage, e
|
|
|
|
)
|
|
|
|
})
|
|
|
|
.and_then(|v| {
|
|
|
|
if v > 100 {
|
|
|
|
Err(format!(
|
|
|
|
"Percentage must be in range of 0 to 100, provided: {}",
|
|
|
|
v
|
|
|
|
))
|
|
|
|
} else {
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
2019-12-10 00:24:44 -08:00
|
|
|
|
2020-06-15 14:37:53 +01:00
|
|
|
pub fn is_amount<T>(amount: T) -> Result<(), String>
|
|
|
|
where
|
|
|
|
T: AsRef<str> + Display,
|
|
|
|
{
|
|
|
|
if amount.as_ref().parse::<u64>().is_ok() || amount.as_ref().parse::<f64>().is_ok() {
|
2019-12-10 11:29:17 -07:00
|
|
|
Ok(())
|
|
|
|
} else {
|
|
|
|
Err(format!(
|
|
|
|
"Unable to parse input amount as integer or float, provided: {}",
|
|
|
|
amount
|
|
|
|
))
|
|
|
|
}
|
2019-12-10 00:24:44 -08:00
|
|
|
}
|
2019-12-30 22:57:47 -07:00
|
|
|
|
2020-06-15 14:37:53 +01:00
|
|
|
pub fn is_amount_or_all<T>(amount: T) -> Result<(), String>
|
|
|
|
where
|
|
|
|
T: AsRef<str> + Display,
|
|
|
|
{
|
|
|
|
if amount.as_ref().parse::<u64>().is_ok()
|
|
|
|
|| amount.as_ref().parse::<f64>().is_ok()
|
|
|
|
|| amount.as_ref() == "ALL"
|
|
|
|
{
|
2020-05-14 12:24:14 -06:00
|
|
|
Ok(())
|
|
|
|
} else {
|
|
|
|
Err(format!(
|
|
|
|
"Unable to parse input amount as integer or float, provided: {}",
|
|
|
|
amount
|
|
|
|
))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-15 14:37:53 +01:00
|
|
|
pub fn is_rfc3339_datetime<T>(value: T) -> Result<(), String>
|
|
|
|
where
|
|
|
|
T: AsRef<str> + Display,
|
|
|
|
{
|
|
|
|
DateTime::parse_from_rfc3339(value.as_ref())
|
2019-12-30 22:57:47 -07:00
|
|
|
.map(|_| ())
|
2020-03-13 00:20:49 -06:00
|
|
|
.map_err(|e| format!("{}", e))
|
2019-12-30 22:57:47 -07:00
|
|
|
}
|
2020-02-07 11:26:56 -07:00
|
|
|
|
2020-06-15 14:37:53 +01:00
|
|
|
pub fn is_derivation<T>(value: T) -> Result<(), String>
|
|
|
|
where
|
|
|
|
T: AsRef<str> + Display,
|
|
|
|
{
|
|
|
|
let value = value.as_ref().replace("'", "");
|
2020-02-07 11:26:56 -07:00
|
|
|
let mut parts = value.split('/');
|
|
|
|
let account = parts.next().unwrap();
|
|
|
|
account
|
2020-02-25 17:41:21 -07:00
|
|
|
.parse::<u32>()
|
2020-02-07 11:26:56 -07:00
|
|
|
.map_err(|e| {
|
|
|
|
format!(
|
2020-03-13 00:20:49 -06:00
|
|
|
"Unable to parse derivation, provided: {}, err: {}",
|
2020-02-07 11:26:56 -07:00
|
|
|
account, e
|
|
|
|
)
|
|
|
|
})
|
|
|
|
.and_then(|_| {
|
|
|
|
if let Some(change) = parts.next() {
|
2020-02-25 17:41:21 -07:00
|
|
|
change.parse::<u32>().map_err(|e| {
|
2020-02-07 11:26:56 -07:00
|
|
|
format!(
|
2020-03-13 00:20:49 -06:00
|
|
|
"Unable to parse derivation, provided: {}, err: {}",
|
2020-02-07 11:26:56 -07:00
|
|
|
change, e
|
|
|
|
)
|
|
|
|
})
|
|
|
|
} else {
|
|
|
|
Ok(0)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
.map(|_| ())
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
|
|
|
use super::*;
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_is_derivation() {
|
2020-06-15 14:37:53 +01:00
|
|
|
assert_eq!(is_derivation("2"), Ok(()));
|
|
|
|
assert_eq!(is_derivation("0"), Ok(()));
|
|
|
|
assert_eq!(is_derivation("65537"), Ok(()));
|
|
|
|
assert_eq!(is_derivation("0/2"), Ok(()));
|
|
|
|
assert_eq!(is_derivation("0'/2'"), Ok(()));
|
|
|
|
assert!(is_derivation("a").is_err());
|
|
|
|
assert!(is_derivation("4294967296").is_err());
|
|
|
|
assert!(is_derivation("a/b").is_err());
|
|
|
|
assert!(is_derivation("0/4294967296").is_err());
|
2020-02-07 11:26:56 -07:00
|
|
|
}
|
|
|
|
}
|