feat: implement websocket_url as a get/set-able global parameter w/ value computation
This commit is contained in:
committed by
Michael Vines
parent
2a5605db24
commit
db291234ed
@ -18,13 +18,15 @@ lazy_static! {
|
|||||||
#[derive(Serialize, Deserialize, Default, Debug, PartialEq)]
|
#[derive(Serialize, Deserialize, Default, Debug, PartialEq)]
|
||||||
pub struct Config {
|
pub struct Config {
|
||||||
pub url: String,
|
pub url: String,
|
||||||
|
pub websocket_url: String,
|
||||||
pub keypair_path: String,
|
pub keypair_path: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Config {
|
impl Config {
|
||||||
pub fn new(url: &str, keypair_path: &str) -> Self {
|
pub fn new(url: &str, websocket_url: &str, keypair_path: &str) -> Self {
|
||||||
Self {
|
Self {
|
||||||
url: url.to_string(),
|
url: url.to_string(),
|
||||||
|
websocket_url: websocket_url.to_string(),
|
||||||
keypair_path: keypair_path.to_string(),
|
keypair_path: keypair_path.to_string(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
105
cli/src/cli.rs
105
cli/src/cli.rs
@ -52,6 +52,7 @@ use std::{
|
|||||||
time::Duration,
|
time::Duration,
|
||||||
{error, fmt},
|
{error, fmt},
|
||||||
};
|
};
|
||||||
|
use url::Url;
|
||||||
|
|
||||||
pub type CliSigners = Vec<Box<dyn Signer>>;
|
pub type CliSigners = Vec<Box<dyn Signer>>;
|
||||||
pub type SignerIndex = usize;
|
pub type SignerIndex = usize;
|
||||||
@ -185,9 +186,7 @@ pub enum CliCommand {
|
|||||||
commitment_config: CommitmentConfig,
|
commitment_config: CommitmentConfig,
|
||||||
},
|
},
|
||||||
LeaderSchedule,
|
LeaderSchedule,
|
||||||
LiveSlots {
|
LiveSlots,
|
||||||
url: String,
|
|
||||||
},
|
|
||||||
Ping {
|
Ping {
|
||||||
lamports: u64,
|
lamports: u64,
|
||||||
interval: Duration,
|
interval: Duration,
|
||||||
@ -435,9 +434,16 @@ impl From<Box<dyn error::Error>> for CliError {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub enum SettingType {
|
||||||
|
Explicit,
|
||||||
|
Computed,
|
||||||
|
SystemDefault,
|
||||||
|
}
|
||||||
|
|
||||||
pub struct CliConfig<'a> {
|
pub struct CliConfig<'a> {
|
||||||
pub command: CliCommand,
|
pub command: CliCommand,
|
||||||
pub json_rpc_url: String,
|
pub json_rpc_url: String,
|
||||||
|
pub websocket_url: String,
|
||||||
pub signers: Vec<&'a dyn Signer>,
|
pub signers: Vec<&'a dyn Signer>,
|
||||||
pub keypair_path: String,
|
pub keypair_path: String,
|
||||||
pub derivation_path: Option<DerivationPath>,
|
pub derivation_path: Option<DerivationPath>,
|
||||||
@ -446,16 +452,97 @@ pub struct CliConfig<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl CliConfig<'_> {
|
impl CliConfig<'_> {
|
||||||
pub fn default_keypair_path() -> String {
|
fn default_keypair_path() -> String {
|
||||||
let mut keypair_path = dirs::home_dir().expect("home directory");
|
let mut keypair_path = dirs::home_dir().expect("home directory");
|
||||||
keypair_path.extend(&[".config", "solana", "id.json"]);
|
keypair_path.extend(&[".config", "solana", "id.json"]);
|
||||||
keypair_path.to_str().unwrap().to_string()
|
keypair_path.to_str().unwrap().to_string()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn default_json_rpc_url() -> String {
|
fn default_json_rpc_url() -> String {
|
||||||
"http://127.0.0.1:8899".to_string()
|
"http://127.0.0.1:8899".to_string()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn default_websocket_url() -> String {
|
||||||
|
Self::compute_ws_url(&Self::default_json_rpc_url())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn compute_ws_url(rpc_url: &str) -> String {
|
||||||
|
let rpc_url: Option<Url> = rpc_url.parse().ok();
|
||||||
|
if rpc_url.is_none() {
|
||||||
|
return "".to_string();
|
||||||
|
}
|
||||||
|
let rpc_url = rpc_url.unwrap();
|
||||||
|
let is_secure = rpc_url.scheme().to_ascii_lowercase() == "https";
|
||||||
|
let mut ws_url = rpc_url.clone();
|
||||||
|
ws_url
|
||||||
|
.set_scheme(if is_secure { "wss" } else { "ws" })
|
||||||
|
.expect("unable to set scheme");
|
||||||
|
let ws_port = match rpc_url.port() {
|
||||||
|
Some(port) => port + 1,
|
||||||
|
None => {
|
||||||
|
if is_secure {
|
||||||
|
8901
|
||||||
|
} else {
|
||||||
|
8900
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
ws_url.set_port(Some(ws_port)).expect("unable to set port");
|
||||||
|
ws_url.to_string()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn first_nonempty_setting(
|
||||||
|
settings: std::vec::Vec<(SettingType, String)>,
|
||||||
|
) -> (SettingType, String) {
|
||||||
|
settings
|
||||||
|
.into_iter()
|
||||||
|
.find(|(_, value)| value != "")
|
||||||
|
.expect("no nonempty setting")
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn compute_websocket_url_setting(
|
||||||
|
websocket_cmd_url: &str,
|
||||||
|
websocket_cfg_url: &str,
|
||||||
|
json_rpc_cmd_url: &str,
|
||||||
|
json_rpc_cfg_url: &str,
|
||||||
|
) -> (SettingType, String) {
|
||||||
|
Self::first_nonempty_setting(vec![
|
||||||
|
(SettingType::Explicit, websocket_cmd_url.to_string()),
|
||||||
|
(SettingType::Explicit, websocket_cfg_url.to_string()),
|
||||||
|
(
|
||||||
|
SettingType::Computed,
|
||||||
|
Self::compute_ws_url(json_rpc_cmd_url),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
SettingType::Computed,
|
||||||
|
Self::compute_ws_url(json_rpc_cfg_url),
|
||||||
|
),
|
||||||
|
(SettingType::SystemDefault, Self::default_websocket_url()),
|
||||||
|
])
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn compute_json_rpc_url_setting(
|
||||||
|
json_rpc_cmd_url: &str,
|
||||||
|
json_rpc_cfg_url: &str,
|
||||||
|
) -> (SettingType, String) {
|
||||||
|
Self::first_nonempty_setting(vec![
|
||||||
|
(SettingType::Explicit, json_rpc_cmd_url.to_string()),
|
||||||
|
(SettingType::Explicit, json_rpc_cfg_url.to_string()),
|
||||||
|
(SettingType::SystemDefault, Self::default_json_rpc_url()),
|
||||||
|
])
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn compute_keypair_path_setting(
|
||||||
|
keypair_cmd_path: &str,
|
||||||
|
keypair_cfg_path: &str,
|
||||||
|
) -> (SettingType, String) {
|
||||||
|
Self::first_nonempty_setting(vec![
|
||||||
|
(SettingType::Explicit, keypair_cmd_path.to_string()),
|
||||||
|
(SettingType::Explicit, keypair_cfg_path.to_string()),
|
||||||
|
(SettingType::SystemDefault, Self::default_keypair_path()),
|
||||||
|
])
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn pubkey(&self) -> Result<Pubkey, SignerError> {
|
pub(crate) fn pubkey(&self) -> Result<Pubkey, SignerError> {
|
||||||
if !self.signers.is_empty() {
|
if !self.signers.is_empty() {
|
||||||
self.signers[0].try_pubkey()
|
self.signers[0].try_pubkey()
|
||||||
@ -475,6 +562,7 @@ impl Default for CliConfig<'_> {
|
|||||||
use_lamports_unit: false,
|
use_lamports_unit: false,
|
||||||
},
|
},
|
||||||
json_rpc_url: Self::default_json_rpc_url(),
|
json_rpc_url: Self::default_json_rpc_url(),
|
||||||
|
websocket_url: Self::default_websocket_url(),
|
||||||
signers: Vec::new(),
|
signers: Vec::new(),
|
||||||
keypair_path: Self::default_keypair_path(),
|
keypair_path: Self::default_keypair_path(),
|
||||||
derivation_path: None,
|
derivation_path: None,
|
||||||
@ -514,7 +602,10 @@ pub fn parse_command(
|
|||||||
signers: vec![],
|
signers: vec![],
|
||||||
}),
|
}),
|
||||||
("ping", Some(matches)) => parse_cluster_ping(matches, default_signer_path, wallet_manager),
|
("ping", Some(matches)) => parse_cluster_ping(matches, default_signer_path, wallet_manager),
|
||||||
("live-slots", Some(matches)) => parse_live_slots(matches),
|
("live-slots", Some(_matches)) => Ok(CliCommandInfo {
|
||||||
|
command: CliCommand::LiveSlots,
|
||||||
|
signers: vec![],
|
||||||
|
}),
|
||||||
("block-production", Some(matches)) => parse_show_block_production(matches),
|
("block-production", Some(matches)) => parse_show_block_production(matches),
|
||||||
("gossip", Some(_matches)) => Ok(CliCommandInfo {
|
("gossip", Some(_matches)) => Ok(CliCommandInfo {
|
||||||
command: CliCommand::ShowGossip,
|
command: CliCommand::ShowGossip,
|
||||||
@ -1474,7 +1565,7 @@ pub fn process_command(config: &CliConfig) -> ProcessResult {
|
|||||||
process_get_transaction_count(&rpc_client, commitment_config)
|
process_get_transaction_count(&rpc_client, commitment_config)
|
||||||
}
|
}
|
||||||
CliCommand::LeaderSchedule => process_leader_schedule(&rpc_client),
|
CliCommand::LeaderSchedule => process_leader_schedule(&rpc_client),
|
||||||
CliCommand::LiveSlots { url } => process_live_slots(&url),
|
CliCommand::LiveSlots => process_live_slots(&config.websocket_url),
|
||||||
CliCommand::Ping {
|
CliCommand::Ping {
|
||||||
lamports,
|
lamports,
|
||||||
interval,
|
interval,
|
||||||
|
@ -170,16 +170,7 @@ impl ClusterQuerySubCommands for App<'_, '_> {
|
|||||||
)
|
)
|
||||||
.subcommand(
|
.subcommand(
|
||||||
SubCommand::with_name("live-slots")
|
SubCommand::with_name("live-slots")
|
||||||
.about("Show information about the current slot progression")
|
.about("Show information about the current slot progression"),
|
||||||
.arg(
|
|
||||||
Arg::with_name("websocket_url")
|
|
||||||
.short("w")
|
|
||||||
.long("ws")
|
|
||||||
.value_name("URL")
|
|
||||||
.takes_value(true)
|
|
||||||
.default_value("ws://127.0.0.1:8900")
|
|
||||||
.help("WebSocket URL for PubSub RPC connection"),
|
|
||||||
),
|
|
||||||
)
|
)
|
||||||
.subcommand(
|
.subcommand(
|
||||||
SubCommand::with_name("block-production")
|
SubCommand::with_name("block-production")
|
||||||
@ -279,14 +270,6 @@ pub fn parse_cluster_ping(
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse_live_slots(matches: &ArgMatches<'_>) -> Result<CliCommandInfo, CliError> {
|
|
||||||
let url: String = value_t_or_exit!(matches, "websocket_url", String);
|
|
||||||
Ok(CliCommandInfo {
|
|
||||||
command: CliCommand::LiveSlots { url },
|
|
||||||
signers: vec![],
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn parse_get_block_time(matches: &ArgMatches<'_>) -> Result<CliCommandInfo, CliError> {
|
pub fn parse_get_block_time(matches: &ArgMatches<'_>) -> Result<CliCommandInfo, CliError> {
|
||||||
let slot = value_t_or_exit!(matches, "slot", u64);
|
let slot = value_t_or_exit!(matches, "slot", u64);
|
||||||
Ok(CliCommandInfo {
|
Ok(CliCommandInfo {
|
||||||
@ -873,6 +856,9 @@ pub fn process_live_slots(url: &str) -> ProcessResult {
|
|||||||
let (mut client, receiver) = PubsubClient::slot_subscribe(url)?;
|
let (mut client, receiver) = PubsubClient::slot_subscribe(url)?;
|
||||||
slot_progress.set_message("Connected.");
|
slot_progress.set_message("Connected.");
|
||||||
|
|
||||||
|
let spacer = "|";
|
||||||
|
slot_progress.println(spacer);
|
||||||
|
|
||||||
let mut last_root = std::u64::MAX;
|
let mut last_root = std::u64::MAX;
|
||||||
let mut last_root_update = Instant::now();
|
let mut last_root_update = Instant::now();
|
||||||
let mut slots_per_second = std::f64::NAN;
|
let mut slots_per_second = std::f64::NAN;
|
||||||
@ -918,11 +904,19 @@ pub fn process_live_slots(url: &str) -> ProcessResult {
|
|||||||
//
|
//
|
||||||
if slot_delta != root_delta {
|
if slot_delta != root_delta {
|
||||||
let prev_root = format!(
|
let prev_root = format!(
|
||||||
"|<- {} <- … <- {} <- {}",
|
"|<--- {} <- … <- {} <- {} (prev)",
|
||||||
previous.root, previous.parent, previous.slot
|
previous.root, previous.parent, previous.slot
|
||||||
)
|
);
|
||||||
.to_owned();
|
|
||||||
slot_progress.println(&prev_root);
|
slot_progress.println(&prev_root);
|
||||||
|
|
||||||
|
let new_root = format!(
|
||||||
|
"| '- {} <- … <- {} <- {} (next)",
|
||||||
|
new_info.root, new_info.parent, new_info.slot
|
||||||
|
);
|
||||||
|
|
||||||
|
slot_progress.println(prev_root);
|
||||||
|
slot_progress.println(new_root);
|
||||||
|
slot_progress.println(spacer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
current = Some(new_info);
|
current = Some(new_info);
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
use crate::cli::SettingType;
|
||||||
use console::style;
|
use console::style;
|
||||||
use solana_sdk::transaction::Transaction;
|
use solana_sdk::transaction::Transaction;
|
||||||
|
|
||||||
@ -11,17 +12,19 @@ pub fn println_name_value(name: &str, value: &str) {
|
|||||||
println!("{} {}", style(name).bold(), styled_value);
|
println!("{} {}", style(name).bold(), styled_value);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn println_name_value_or(name: &str, value: &str, default_value: &str) {
|
pub fn println_name_value_or(name: &str, value: &str, setting_type: SettingType) {
|
||||||
if value == "" {
|
let description = match setting_type {
|
||||||
println!(
|
SettingType::Explicit => "",
|
||||||
"{} {} {}",
|
SettingType::Computed => "(computed)",
|
||||||
style(name).bold(),
|
SettingType::SystemDefault => "(default)",
|
||||||
style(default_value),
|
|
||||||
style("(default)").italic()
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
println!("{} {}", style(name).bold(), style(value));
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
println!(
|
||||||
|
"{} {} {}",
|
||||||
|
style(name).bold(),
|
||||||
|
style(value),
|
||||||
|
style(description).italic(),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn println_signers(tx: &Transaction) {
|
pub fn println_signers(tx: &Transaction) {
|
||||||
|
102
cli/src/main.rs
102
cli/src/main.rs
@ -20,29 +20,31 @@ fn parse_settings(matches: &ArgMatches<'_>) -> Result<bool, Box<dyn error::Error
|
|||||||
("get", Some(subcommand_matches)) => {
|
("get", Some(subcommand_matches)) => {
|
||||||
if let Some(config_file) = matches.value_of("config_file") {
|
if let Some(config_file) = matches.value_of("config_file") {
|
||||||
let config = Config::load(config_file).unwrap_or_default();
|
let config = Config::load(config_file).unwrap_or_default();
|
||||||
|
|
||||||
|
let (url_setting_type, json_rpc_url) =
|
||||||
|
CliConfig::compute_json_rpc_url_setting("", &config.url);
|
||||||
|
let (ws_setting_type, websocket_url) = CliConfig::compute_websocket_url_setting(
|
||||||
|
"",
|
||||||
|
&config.websocket_url,
|
||||||
|
"",
|
||||||
|
&config.url,
|
||||||
|
);
|
||||||
|
let (keypair_setting_type, keypair_path) =
|
||||||
|
CliConfig::compute_keypair_path_setting("", &config.keypair_path);
|
||||||
|
|
||||||
if let Some(field) = subcommand_matches.value_of("specific_setting") {
|
if let Some(field) = subcommand_matches.value_of("specific_setting") {
|
||||||
let (field_name, value, default_value) = match field {
|
let (field_name, value, setting_type) = match field {
|
||||||
"url" => ("RPC URL", config.url, CliConfig::default_json_rpc_url()),
|
"json_rpc_url" => ("RPC URL", json_rpc_url, url_setting_type),
|
||||||
"keypair" => (
|
"websocket_url" => ("WS URL", websocket_url, ws_setting_type),
|
||||||
"Key Path",
|
"keypair" => ("Key Path", keypair_path, keypair_setting_type),
|
||||||
config.keypair_path,
|
|
||||||
CliConfig::default_keypair_path(),
|
|
||||||
),
|
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
};
|
};
|
||||||
println_name_value_or(&format!("{}:", field_name), &value, &default_value);
|
println_name_value_or(&format!("{}:", field_name), &value, setting_type);
|
||||||
} else {
|
} else {
|
||||||
println_name_value("Config File:", config_file);
|
println_name_value("Config File:", config_file);
|
||||||
println_name_value_or(
|
println_name_value_or("RPC URL:", &json_rpc_url, url_setting_type);
|
||||||
"RPC URL:",
|
println_name_value_or("WS URL:", &websocket_url, ws_setting_type);
|
||||||
&config.url,
|
println_name_value_or("Keypair Path:", &keypair_path, keypair_setting_type);
|
||||||
&CliConfig::default_json_rpc_url(),
|
|
||||||
);
|
|
||||||
println_name_value_or(
|
|
||||||
"Keypair Path:",
|
|
||||||
&config.keypair_path,
|
|
||||||
&CliConfig::default_keypair_path(),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
println!(
|
println!(
|
||||||
@ -58,13 +60,29 @@ fn parse_settings(matches: &ArgMatches<'_>) -> Result<bool, Box<dyn error::Error
|
|||||||
if let Some(url) = subcommand_matches.value_of("json_rpc_url") {
|
if let Some(url) = subcommand_matches.value_of("json_rpc_url") {
|
||||||
config.url = url.to_string();
|
config.url = url.to_string();
|
||||||
}
|
}
|
||||||
|
if let Some(url) = subcommand_matches.value_of("websocket_url") {
|
||||||
|
config.websocket_url = url.to_string();
|
||||||
|
}
|
||||||
if let Some(keypair) = subcommand_matches.value_of("keypair") {
|
if let Some(keypair) = subcommand_matches.value_of("keypair") {
|
||||||
config.keypair_path = keypair.to_string();
|
config.keypair_path = keypair.to_string();
|
||||||
}
|
}
|
||||||
config.save(config_file)?;
|
config.save(config_file)?;
|
||||||
|
|
||||||
|
let (url_setting_type, json_rpc_url) =
|
||||||
|
CliConfig::compute_json_rpc_url_setting("", &config.url);
|
||||||
|
let (ws_setting_type, websocket_url) = CliConfig::compute_websocket_url_setting(
|
||||||
|
"",
|
||||||
|
&config.websocket_url,
|
||||||
|
"",
|
||||||
|
&config.url,
|
||||||
|
);
|
||||||
|
let (keypair_setting_type, keypair_path) =
|
||||||
|
CliConfig::compute_keypair_path_setting("", &config.keypair_path);
|
||||||
|
|
||||||
println_name_value("Config File:", config_file);
|
println_name_value("Config File:", config_file);
|
||||||
println_name_value("RPC URL:", &config.url);
|
println_name_value_or("RPC URL:", &json_rpc_url, url_setting_type);
|
||||||
println_name_value("Keypair Path:", &config.keypair_path);
|
println_name_value_or("WS URL:", &websocket_url, ws_setting_type);
|
||||||
|
println_name_value_or("Keypair Path:", &keypair_path, keypair_setting_type);
|
||||||
} else {
|
} else {
|
||||||
println!(
|
println!(
|
||||||
"{} Either provide the `--config` arg or ensure home directory exists to use the default config location",
|
"{} Either provide the `--config` arg or ensure home directory exists to use the default config location",
|
||||||
@ -89,22 +107,20 @@ pub fn parse_args<'a>(
|
|||||||
} else {
|
} else {
|
||||||
Config::default()
|
Config::default()
|
||||||
};
|
};
|
||||||
let json_rpc_url = if let Some(url) = matches.value_of("json_rpc_url") {
|
let (_, json_rpc_url) = CliConfig::compute_json_rpc_url_setting(
|
||||||
url.to_string()
|
matches.value_of("json_rpc_url").unwrap_or(""),
|
||||||
} else if config.url != "" {
|
&config.url,
|
||||||
config.url
|
);
|
||||||
} else {
|
let (_, websocket_url) = CliConfig::compute_websocket_url_setting(
|
||||||
let default = CliConfig::default();
|
matches.value_of("websocket_url").unwrap_or(""),
|
||||||
default.json_rpc_url
|
&config.websocket_url,
|
||||||
};
|
matches.value_of("json_rpc_url").unwrap_or(""),
|
||||||
|
&config.url,
|
||||||
let default_signer_path = if matches.is_present("keypair") {
|
);
|
||||||
matches.value_of("keypair").unwrap().to_string()
|
let (_, default_signer_path) = CliConfig::compute_keypair_path_setting(
|
||||||
} else if config.keypair_path != "" {
|
matches.value_of("keypair").unwrap_or(""),
|
||||||
config.keypair_path
|
&config.keypair_path,
|
||||||
} else {
|
);
|
||||||
CliConfig::default_keypair_path()
|
|
||||||
};
|
|
||||||
|
|
||||||
let CliCommandInfo { command, signers } =
|
let CliCommandInfo { command, signers } =
|
||||||
parse_command(&matches, &default_signer_path, wallet_manager.as_ref())?;
|
parse_command(&matches, &default_signer_path, wallet_manager.as_ref())?;
|
||||||
@ -113,6 +129,7 @@ pub fn parse_args<'a>(
|
|||||||
CliConfig {
|
CliConfig {
|
||||||
command,
|
command,
|
||||||
json_rpc_url,
|
json_rpc_url,
|
||||||
|
websocket_url,
|
||||||
signers: vec![],
|
signers: vec![],
|
||||||
keypair_path: default_signer_path,
|
keypair_path: default_signer_path,
|
||||||
derivation_path: derivation_of(matches, "derivation_path"),
|
derivation_path: derivation_of(matches, "derivation_path"),
|
||||||
@ -154,6 +171,15 @@ fn main() -> Result<(), Box<dyn error::Error>> {
|
|||||||
.validator(is_url)
|
.validator(is_url)
|
||||||
.help("JSON RPC URL for the solana cluster"),
|
.help("JSON RPC URL for the solana cluster"),
|
||||||
)
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name("websocket_url")
|
||||||
|
.long("ws")
|
||||||
|
.value_name("URL")
|
||||||
|
.takes_value(true)
|
||||||
|
.global(true)
|
||||||
|
.validator(is_url)
|
||||||
|
.help("WebSocket URL for the solana cluster"),
|
||||||
|
)
|
||||||
.arg(
|
.arg(
|
||||||
Arg::with_name("keypair")
|
Arg::with_name("keypair")
|
||||||
.short("k")
|
.short("k")
|
||||||
@ -198,7 +224,7 @@ fn main() -> Result<(), Box<dyn error::Error>> {
|
|||||||
.index(1)
|
.index(1)
|
||||||
.value_name("CONFIG_FIELD")
|
.value_name("CONFIG_FIELD")
|
||||||
.takes_value(true)
|
.takes_value(true)
|
||||||
.possible_values(&["url", "keypair"])
|
.possible_values(&["json_rpc_url", "websocket_url", "keypair"])
|
||||||
.help("Return a specific config setting"),
|
.help("Return a specific config setting"),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
@ -207,7 +233,7 @@ fn main() -> Result<(), Box<dyn error::Error>> {
|
|||||||
.about("Set a config setting")
|
.about("Set a config setting")
|
||||||
.group(
|
.group(
|
||||||
ArgGroup::with_name("config_settings")
|
ArgGroup::with_name("config_settings")
|
||||||
.args(&["json_rpc_url", "keypair"])
|
.args(&["json_rpc_url", "websocket_url", "keypair"])
|
||||||
.multiple(true)
|
.multiple(true)
|
||||||
.required(true),
|
.required(true),
|
||||||
),
|
),
|
||||||
|
Reference in New Issue
Block a user