Files
solana/cli/src/clap_app.rs
theonekeyg 9d0a937a05 Auto-generate shell completions for solana-cli (issue #8879 and #14005) (#18931)
* Auto-generate shell completions for solana-cli (issue #8879 and #14005)

Implement `completion` SubCommand for solana-cli, which outputs
completion script to stdout and exits the process. The script generation
handled completely by clap.

In order to implement the generation, one minor design change was
necessary regarding the creation of clap `App`.

Previously: One part of App initialization was in the `app` function,
and some other arguments and subcommands were added later directly in
the `main` function.

Now: The whole construction of App was moved to `get_clap_app` function.

P.S. I wasn't sure if constructing App separately had visual importance,
so both constructing parts are still separate in `base_clap_app` and
`final_clap_app` functions. But they sure could be in one single
function.

* Dereplicode match expr, fix clippy warning.

* Move clap App construction into separate module

Also join two parts of the construction into a single function

* Fix tests

* Apply rustfmt lints
2021-07-28 09:43:32 -06:00

458 lines
18 KiB
Rust

use crate::{
cli::*, cluster_query::*, feature::*, inflation::*, nonce::*, program::*, stake::*,
validator_info::*, vote::*,
};
use clap::{App, AppSettings, Arg, ArgGroup, SubCommand};
use solana_clap_utils::{
self, fee_payer::fee_payer_arg, input_validators::*, keypair::*, memo::memo_arg, nonce::*,
offline::*,
};
use solana_cli_config::CONFIG_FILE;
pub fn get_clap_app<'ab, 'v>(name: &str, about: &'ab str, version: &'v str) -> App<'ab, 'v> {
App::new(name)
.about(about)
.version(version)
.setting(AppSettings::SubcommandRequiredElseHelp)
.subcommand(
SubCommand::with_name("address")
.about("Get your public key")
.arg(
Arg::with_name("confirm_key")
.long("confirm-key")
.takes_value(false)
.help("Confirm key on device; only relevant if using remote wallet"),
),
)
.cluster_query_subcommands()
.feature_subcommands()
.inflation_subcommands()
.nonce_subcommands()
.program_subcommands()
.stake_subcommands()
.subcommand(
SubCommand::with_name("airdrop")
.about("Request SOL from a faucet")
.arg(
Arg::with_name("amount")
.index(1)
.value_name("AMOUNT")
.takes_value(true)
.validator(is_amount)
.required(true)
.help("The airdrop amount to request, in SOL"),
)
.arg(
pubkey!(Arg::with_name("to")
.index(2)
.value_name("RECIPIENT_ADDRESS"),
"The account address of airdrop recipient. "),
),
)
.subcommand(
SubCommand::with_name("balance")
.about("Get your balance")
.arg(
pubkey!(Arg::with_name("pubkey")
.index(1)
.value_name("ACCOUNT_ADDRESS"),
"The account address of the balance to check. ")
)
.arg(
Arg::with_name("lamports")
.long("lamports")
.takes_value(false)
.help("Display balance in lamports instead of SOL"),
),
)
.subcommand(
SubCommand::with_name("confirm")
.about("Confirm transaction by signature")
.arg(
Arg::with_name("signature")
.index(1)
.value_name("TRANSACTION_SIGNATURE")
.takes_value(true)
.required(true)
.help("The transaction signature to confirm"),
)
.after_help(// Formatted specifically for the manually-indented heredoc string
"Note: This will show more detailed information for finalized transactions with verbose mode (-v/--verbose).\
\n\
\nAccount modes:\
\n |srwx|\
\n s: signed\
\n r: readable (always true)\
\n w: writable\
\n x: program account (inner instructions excluded)\
"
),
)
.subcommand(
SubCommand::with_name("decode-transaction")
.about("Decode a serialized transaction")
.arg(
Arg::with_name("transaction")
.index(1)
.value_name("TRANSACTION")
.takes_value(true)
.required(true)
.help("transaction to decode"),
)
.arg(
Arg::with_name("encoding")
.index(2)
.value_name("ENCODING")
.possible_values(&["base58", "base64"]) // Subset of `UiTransactionEncoding` enum
.default_value("base58")
.takes_value(true)
.required(true)
.help("transaction encoding"),
),
)
.subcommand(
SubCommand::with_name("create-address-with-seed")
.about("Generate a derived account address with a seed")
.arg(
Arg::with_name("seed")
.index(1)
.value_name("SEED_STRING")
.takes_value(true)
.required(true)
.validator(is_derived_address_seed)
.help("The seed. Must not take more than 32 bytes to encode as utf-8"),
)
.arg(
Arg::with_name("program_id")
.index(2)
.value_name("PROGRAM_ID")
.takes_value(true)
.required(true)
.help(
"The program_id that the address will ultimately be used for, \n\
or one of NONCE, STAKE, and VOTE keywords",
),
)
.arg(
pubkey!(Arg::with_name("from")
.long("from")
.value_name("FROM_PUBKEY")
.required(false),
"From (base) key, [default: cli config keypair]. "),
),
)
.subcommand(
SubCommand::with_name("deploy")
.about("Deploy a program")
.arg(
Arg::with_name("program_location")
.index(1)
.value_name("PROGRAM_FILEPATH")
.takes_value(true)
.required(true)
.help("/path/to/program.o"),
)
.arg(
Arg::with_name("address_signer")
.index(2)
.value_name("PROGRAM_ADDRESS_SIGNER")
.takes_value(true)
.validator(is_valid_signer)
.help("The signer for the desired address of the program [default: new random address]")
)
.arg(
Arg::with_name("use_deprecated_loader")
.long("use-deprecated-loader")
.takes_value(false)
.hidden(true) // Don't document this argument to discourage its use
.help("Use the deprecated BPF loader")
)
.arg(
Arg::with_name("allow_excessive_balance")
.long("allow-excessive-deploy-account-balance")
.takes_value(false)
.help("Use the designated program id, even if the account already holds a large balance of SOL")
),
)
.subcommand(
SubCommand::with_name("resolve-signer")
.about("Checks that a signer is valid, and returns its specific path; useful for signers that may be specified generally, eg. usb://ledger")
.arg(
Arg::with_name("signer")
.index(1)
.value_name("SIGNER_KEYPAIR")
.takes_value(true)
.required(true)
.validator(is_valid_signer)
.help("The signer path to resolve")
)
)
.subcommand(
SubCommand::with_name("transfer")
.about("Transfer funds between system accounts")
.alias("pay")
.arg(
pubkey!(Arg::with_name("to")
.index(1)
.value_name("RECIPIENT_ADDRESS")
.required(true),
"The account address of recipient. "),
)
.arg(
Arg::with_name("amount")
.index(2)
.value_name("AMOUNT")
.takes_value(true)
.validator(is_amount_or_all)
.required(true)
.help("The amount to send, in SOL; accepts keyword ALL"),
)
.arg(
pubkey!(Arg::with_name("from")
.long("from")
.value_name("FROM_ADDRESS"),
"Source account of funds (if different from client local account). "),
)
.arg(
Arg::with_name("no_wait")
.long("no-wait")
.takes_value(false)
.help("Return signature immediately after submitting the transaction, instead of waiting for confirmations"),
)
.arg(
Arg::with_name("derived_address_seed")
.long("derived-address-seed")
.takes_value(true)
.value_name("SEED_STRING")
.requires("derived_address_program_id")
.validator(is_derived_address_seed)
.hidden(true)
)
.arg(
Arg::with_name("derived_address_program_id")
.long("derived-address-program-id")
.takes_value(true)
.value_name("PROGRAM_ID")
.requires("derived_address_seed")
.hidden(true)
)
.arg(
Arg::with_name("allow_unfunded_recipient")
.long("allow-unfunded-recipient")
.takes_value(false)
.help("Complete the transfer even if the recipient address is not funded")
)
.offline_args()
.nonce_args(false)
.arg(memo_arg())
.arg(fee_payer_arg()),
)
.subcommand(
SubCommand::with_name("account")
.about("Show the contents of an account")
.alias("account")
.arg(
pubkey!(Arg::with_name("account_pubkey")
.index(1)
.value_name("ACCOUNT_ADDRESS")
.required(true),
"Account key URI. ")
)
.arg(
Arg::with_name("output_file")
.long("output-file")
.short("o")
.value_name("FILEPATH")
.takes_value(true)
.help("Write the account data to this file"),
)
.arg(
Arg::with_name("lamports")
.long("lamports")
.takes_value(false)
.help("Display balance in lamports instead of SOL"),
),
)
.validator_info_subcommands()
.vote_subcommands()
.arg({
let arg = Arg::with_name("config_file")
.short("C")
.long("config")
.value_name("FILEPATH")
.takes_value(true)
.global(true)
.help("Configuration file to use");
if let Some(ref config_file) = *CONFIG_FILE {
arg.default_value(config_file)
} else {
arg
}
})
.arg(
Arg::with_name("json_rpc_url")
.short("u")
.long("url")
.value_name("URL_OR_MONIKER")
.takes_value(true)
.global(true)
.validator(is_url_or_moniker)
.help(
"URL for Solana's JSON RPC or moniker (or their first letter): \
[mainnet-beta, testnet, devnet, localhost]",
),
)
.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::with_name("keypair")
.short("k")
.long("keypair")
.value_name("KEYPAIR")
.global(true)
.takes_value(true)
.help("Filepath or URL to a keypair"),
)
.arg(
Arg::with_name("commitment")
.long("commitment")
.takes_value(true)
.possible_values(&[
"processed",
"confirmed",
"finalized",
"recent", // Deprecated as of v1.5.5
"single", // Deprecated as of v1.5.5
"singleGossip", // Deprecated as of v1.5.5
"root", // Deprecated as of v1.5.5
"max", // Deprecated as of v1.5.5
])
.value_name("COMMITMENT_LEVEL")
.hide_possible_values(true)
.global(true)
.help("Return information at the selected commitment level [possible values: processed, confirmed, finalized]"),
)
.arg(
Arg::with_name("verbose")
.long("verbose")
.short("v")
.global(true)
.help("Show additional information"),
)
.arg(
Arg::with_name("no_address_labels")
.long("no-address-labels")
.global(true)
.help("Do not use address labels in the output"),
)
.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"),
)
.arg(
Arg::with_name(SKIP_SEED_PHRASE_VALIDATION_ARG.name)
.long(SKIP_SEED_PHRASE_VALIDATION_ARG.long)
.global(true)
.help(SKIP_SEED_PHRASE_VALIDATION_ARG.help),
)
.arg(
Arg::with_name("rpc_timeout")
.long("rpc-timeout")
.value_name("SECONDS")
.takes_value(true)
.default_value(DEFAULT_RPC_TIMEOUT_SECONDS)
.global(true)
.hidden(true)
.help("Timeout value for RPC requests"),
)
.arg(
Arg::with_name("confirm_transaction_initial_timeout")
.long("confirm-timeout")
.value_name("SECONDS")
.takes_value(true)
.default_value(DEFAULT_CONFIRM_TX_TIMEOUT_SECONDS)
.global(true)
.hidden(true)
.help("Timeout value for initial transaction status"),
)
.subcommand(
SubCommand::with_name("config")
.about("Solana command-line tool configuration settings")
.aliases(&["get", "set"])
.setting(AppSettings::SubcommandRequiredElseHelp)
.subcommand(
SubCommand::with_name("get")
.about("Get current config settings")
.arg(
Arg::with_name("specific_setting")
.index(1)
.value_name("CONFIG_FIELD")
.takes_value(true)
.possible_values(&[
"json_rpc_url",
"websocket_url",
"keypair",
"commitment",
])
.help("Return a specific config setting"),
),
)
.subcommand(
SubCommand::with_name("set")
.about("Set a config setting")
.group(
ArgGroup::with_name("config_settings")
.args(&["json_rpc_url", "websocket_url", "keypair", "commitment"])
.multiple(true)
.required(true),
),
)
.subcommand(
SubCommand::with_name("import-address-labels")
.about("Import a list of address labels")
.arg(
Arg::with_name("filename")
.index(1)
.value_name("FILENAME")
.takes_value(true)
.help("YAML file of address labels"),
),
)
.subcommand(
SubCommand::with_name("export-address-labels")
.about("Export the current address labels")
.arg(
Arg::with_name("filename")
.index(1)
.value_name("FILENAME")
.takes_value(true)
.help("YAML file to receive the current address labels"),
),
),
)
.subcommand(
SubCommand::with_name("completion")
.about("Generate completion scripts for various shells")
.arg(
Arg::with_name("shell")
.long("shell")
.short("s")
.takes_value(true)
.possible_values(&["bash", "fish", "zsh", "powershell", "elvish"])
.default_value("bash")
)
)
}