Add cli-output helpers (#18933)

* Add OutputFormat helper to reduce copy-pasta

* Add CliSignOnlyData constructor
This commit is contained in:
Tyera Eulberg 2021-07-27 22:21:23 -06:00 committed by GitHub
parent 14f0ce850d
commit 467c18e0d1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 110 additions and 31 deletions

1
Cargo.lock generated
View File

@ -4354,6 +4354,7 @@ dependencies = [
"Inflector",
"base64 0.13.0",
"chrono",
"clap 2.33.3",
"console",
"humantime",
"indicatif",

View File

@ -12,6 +12,7 @@ documentation = "https://docs.rs/solana-cli-output"
[dependencies]
base64 = "0.13.0"
chrono = { version = "0.4.11", features = ["serde"] }
clap = "2.33.0"
console = "0.14.1"
humantime = "2.0.1"
Inflector = "0.11.4"

View File

@ -8,6 +8,7 @@ use {
QuietDisplay, VerboseDisplay,
},
chrono::{Local, TimeZone},
clap::ArgMatches,
console::{style, Emoji},
inflector::cases::titlecase::to_title_case,
serde::{Deserialize, Serialize},
@ -47,7 +48,7 @@ use {
static WARNING: Emoji = Emoji("⚠️", "!");
#[derive(PartialEq)]
#[derive(PartialEq, Debug)]
pub enum OutputFormat {
Display,
Json,
@ -77,6 +78,21 @@ impl OutputFormat {
OutputFormat::JsonCompact => serde_json::to_value(item).unwrap().to_string(),
}
}
pub fn from_matches(matches: &ArgMatches<'_>, output_name: &str, verbose: bool) -> Self {
matches
.value_of(output_name)
.map(|value| match value {
"json" => OutputFormat::Json,
"json-compact" => OutputFormat::JsonCompact,
_ => unreachable!(),
})
.unwrap_or(if verbose {
OutputFormat::DisplayVerbose
} else {
OutputFormat::Display
})
}
}
#[derive(Serialize, Deserialize)]
@ -1572,7 +1588,7 @@ impl fmt::Display for CliInflation {
}
}
#[derive(Serialize, Deserialize, Default)]
#[derive(Serialize, Deserialize, Default, Debug, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct CliSignOnlyData {
pub blockhash: String,
@ -2024,6 +2040,11 @@ pub fn return_signers_with_config(
output_format: &OutputFormat,
config: &ReturnSignersConfig,
) -> Result<String, Box<dyn std::error::Error>> {
let cli_command = return_signers_data(tx, config);
Ok(output_format.formatted_string(&cli_command))
}
pub fn return_signers_data(tx: &Transaction, config: &ReturnSignersConfig) -> CliSignOnlyData {
let verify_results = tx.verify_with_results();
let mut signers = Vec::new();
let mut absent = Vec::new();
@ -2048,15 +2069,13 @@ pub fn return_signers_with_config(
None
};
let cli_command = CliSignOnlyData {
CliSignOnlyData {
blockhash: tx.message.recent_blockhash.to_string(),
message,
signers,
absent,
bad_sig,
};
Ok(output_format.formatted_string(&cli_command))
}
}
pub fn parse_sign_only_reply_string(reply: &str) -> SignOnly {
@ -2438,6 +2457,7 @@ impl VerboseDisplay for CliGossipNodes {}
#[cfg(test)]
mod tests {
use super::*;
use clap::{App, Arg};
use solana_sdk::{
message::Message,
pubkey::Pubkey,
@ -2498,6 +2518,22 @@ mod tests {
assert_eq!(sign_only.absent_signers[0], absent.pubkey());
assert_eq!(sign_only.bad_signers[0], bad.pubkey());
let res_data = return_signers_data(&tx, &ReturnSignersConfig::default());
assert_eq!(
res_data,
CliSignOnlyData {
blockhash: blockhash.to_string(),
message: None,
signers: vec![format!(
"{}={}",
present.pubkey().to_string(),
tx.signatures[1]
)],
absent: vec![absent.pubkey().to_string()],
bad_sig: vec![bad.pubkey().to_string()],
}
);
let expected_msg = "AwECBwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDgTl3Dqh9\
F19Wo1Rmw0x+zMuNipG07jeiXfYPW4/Js5QEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQE\
BAQEBAYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBQUFBQUFBQUFBQUFBQUFBQUF\
@ -2511,10 +2547,26 @@ mod tests {
let res = return_signers_with_config(&tx, &OutputFormat::JsonCompact, &config).unwrap();
let sign_only = parse_sign_only_reply_string(&res);
assert_eq!(sign_only.blockhash, blockhash);
assert_eq!(sign_only.message, Some(expected_msg));
assert_eq!(sign_only.message, Some(expected_msg.clone()));
assert_eq!(sign_only.present_signers[0].0, present.pubkey());
assert_eq!(sign_only.absent_signers[0], absent.pubkey());
assert_eq!(sign_only.bad_signers[0], bad.pubkey());
let res_data = return_signers_data(&tx, &config);
assert_eq!(
res_data,
CliSignOnlyData {
blockhash: blockhash.to_string(),
message: Some(expected_msg),
signers: vec![format!(
"{}={}",
present.pubkey().to_string(),
tx.signatures[1]
)],
absent: vec![absent.pubkey().to_string()],
bad_sig: vec![bad.pubkey().to_string()],
}
);
}
#[test]
@ -2563,4 +2615,50 @@ mod tests {
"verbose"
);
}
#[test]
fn test_output_format_from_matches() {
let app = App::new("test").arg(
Arg::with_name("output_format")
.long("output")
.value_name("FORMAT")
.global(true)
.takes_value(true)
.possible_values(&["json", "json-compact"])
.help("Return information in specified output format"),
);
let matches = app
.clone()
.get_matches_from(vec!["test", "--output", "json"]);
assert_eq!(
OutputFormat::from_matches(&matches, "output_format", false),
OutputFormat::Json
);
assert_eq!(
OutputFormat::from_matches(&matches, "output_format", true),
OutputFormat::Json
);
let matches = app
.clone()
.get_matches_from(vec!["test", "--output", "json-compact"]);
assert_eq!(
OutputFormat::from_matches(&matches, "output_format", false),
OutputFormat::JsonCompact
);
assert_eq!(
OutputFormat::from_matches(&matches, "output_format", true),
OutputFormat::JsonCompact
);
let matches = app.clone().get_matches_from(vec!["test"]);
assert_eq!(
OutputFormat::from_matches(&matches, "output_format", false),
OutputFormat::Display
);
assert_eq!(
OutputFormat::from_matches(&matches, "output_format", true),
OutputFormat::DisplayVerbose
);
}
}

View File

@ -200,18 +200,7 @@ pub fn parse_args<'a>(
}
let verbose = matches.is_present("verbose");
let output_format = matches
.value_of("output_format")
.map(|value| match value {
"json" => OutputFormat::Json,
"json-compact" => OutputFormat::JsonCompact,
_ => unreachable!(),
})
.unwrap_or(if verbose {
OutputFormat::DisplayVerbose
} else {
OutputFormat::Display
});
let output_format = OutputFormat::from_matches(matches, "output_format", verbose);
let (_, commitment) = CliConfig::compute_commitment_config(
matches.value_of("commitment").unwrap_or(""),

View File

@ -385,18 +385,7 @@ pub fn bigtable_process_command(ledger_path: &Path, matches: &ArgMatches<'_>) {
let runtime = tokio::runtime::Runtime::new().unwrap();
let verbose = matches.is_present("verbose");
let output_format = matches
.value_of("output_format")
.map(|value| match value {
"json" => OutputFormat::Json,
"json-compact" => OutputFormat::JsonCompact,
_ => unreachable!(),
})
.unwrap_or(if verbose {
OutputFormat::DisplayVerbose
} else {
OutputFormat::Display
});
let output_format = OutputFormat::from_matches(matches, "output_format", verbose);
let future = match matches.subcommand() {
("upload", Some(arg_matches)) => {

View File

@ -2807,6 +2807,7 @@ dependencies = [
"Inflector",
"base64 0.13.0",
"chrono",
"clap",
"console",
"humantime",
"indicatif",