refactor(hermes): move to clap_derive

This commit is contained in:
Reisen 2023-09-23 04:21:32 +00:00 committed by Reisen
parent 7ee97ce03c
commit 26b8dcdea9
8 changed files with 282 additions and 114 deletions

138
hermes/Cargo.lock generated
View File

@ -136,6 +136,54 @@ dependencies = [
"winapi",
]
[[package]]
name = "anstream"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b1f58811cfac344940f1a400b6e6231ce35171f614f26439e80f8c1465c5cc0c"
dependencies = [
"anstyle",
"anstyle-parse",
"anstyle-query",
"anstyle-wincon",
"colorchoice",
"utf8parse",
]
[[package]]
name = "anstyle"
version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b84bf0a05bbb2a83e5eb6fa36bb6e87baa08193c35ff52bbf6b38d8af2890e46"
[[package]]
name = "anstyle-parse"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "938874ff5980b03a87c5524b3ae5b59cf99b1d6bc836848df7bc5ada9643c333"
dependencies = [
"utf8parse",
]
[[package]]
name = "anstyle-query"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b"
dependencies = [
"windows-sys 0.48.0",
]
[[package]]
name = "anstyle-wincon"
version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "58f54d10c6dfa51283a066ceab3ec1ab78d13fae00aa49243a45e4571fb79dfd"
dependencies = [
"anstyle",
"windows-sys 0.48.0",
]
[[package]]
name = "anyhow"
version = "1.0.72"
@ -877,12 +925,58 @@ dependencies = [
"ansi_term",
"atty",
"bitflags 1.3.2",
"strsim",
"strsim 0.8.0",
"textwrap",
"unicode-width",
"vec_map",
]
[[package]]
name = "clap"
version = "4.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b1d7b8d5ec32af0fadc644bf1fd509a688c2103b185644bb1e29d164e0703136"
dependencies = [
"clap_builder",
"clap_derive",
]
[[package]]
name = "clap_builder"
version = "4.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5179bb514e4d7c2051749d8fcefa2ed6d06a9f4e6d69faf3805f5d80b8cf8d56"
dependencies = [
"anstream",
"anstyle",
"clap_lex",
"strsim 0.10.0",
]
[[package]]
name = "clap_derive"
version = "4.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0862016ff20d69b84ef8247369fabf5c008a7417002411897d40ee1f4532b873"
dependencies = [
"heck 0.4.1",
"proc-macro2 1.0.66",
"quote 1.0.31",
"syn 2.0.26",
]
[[package]]
name = "clap_lex"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cd7cc57abe963c6d3b9d8be5b06ba7c8957a930305ca90304f24ef040aa6f961"
[[package]]
name = "colorchoice"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7"
[[package]]
name = "concurrent-queue"
version = "2.2.0"
@ -1764,7 +1858,7 @@ checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
[[package]]
name = "hermes"
version = "0.1.20"
version = "0.1.21"
dependencies = [
"anyhow",
"async-trait",
@ -1774,6 +1868,7 @@ dependencies = [
"borsh 0.10.3",
"byteorder",
"chrono",
"clap 4.4.4",
"dashmap",
"derive_more",
"env_logger 0.10.0",
@ -1799,7 +1894,6 @@ dependencies = [
"solana-account-decoder",
"solana-client",
"solana-sdk",
"structopt",
"strum",
"tokio",
"tower-http",
@ -5001,7 +5095,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "76b0c923e89c64ed398964ec2a3ddecacb5a712664d19812ebbf65eefd599145"
dependencies = [
"chrono",
"clap",
"clap 2.34.0",
"rpassword",
"solana-perf",
"solana-remote-wallet",
@ -5040,7 +5134,7 @@ dependencies = [
"bincode",
"bs58",
"bytes",
"clap",
"clap 2.34.0",
"crossbeam-channel",
"enum_dispatch",
"futures",
@ -5105,7 +5199,7 @@ checksum = "c9aae7a9b16c3e81b6532c3a5d886c58ee0cb518ee6b417f28ea0cfee972ac60"
dependencies = [
"bincode",
"byteorder",
"clap",
"clap 2.34.0",
"crossbeam-channel",
"log",
"serde",
@ -5197,7 +5291,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3093c7701cc7d3603a32aec204e5a2e4935f71b37eabd7302d5b67bdb4ea0a02"
dependencies = [
"bincode",
"clap",
"clap 2.34.0",
"crossbeam-channel",
"log",
"nix 0.23.2",
@ -5615,28 +5709,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
[[package]]
name = "structopt"
version = "0.3.26"
name = "strsim"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0c6b5c64445ba8094a6ab0c3cd2ad323e07171012d9c98b0b15651daf1787a10"
dependencies = [
"clap",
"lazy_static",
"structopt-derive",
]
[[package]]
name = "structopt-derive"
version = "0.4.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dcb5ae327f9cc13b68763b5749770cb9e048a99bd9dfdfa58d0cf05d5f64afe0"
dependencies = [
"heck 0.3.3",
"proc-macro-error",
"proc-macro2 1.0.66",
"quote 1.0.31",
"syn 1.0.109",
]
checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
[[package]]
name = "strum"
@ -6345,6 +6421,12 @@ version = "0.7.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9"
[[package]]
name = "utf8parse"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a"
[[package]]
name = "utoipa"
version = "3.4.3"

View File

@ -1,7 +1,8 @@
[package]
name = "hermes"
version = "0.1.20"
edition = "2021"
name = "hermes"
version = "0.1.21"
description = "Hermes is an agent that provides Verified Prices from the Pythnet Pyth Oracle."
edition = "2021"
[dependencies]
async-trait = { version = "0.1.73" }
@ -33,7 +34,7 @@ serde_json = { version = "1.0.93" }
serde_qs = { version = "0.12.0", features = ["axum"] }
serde_wormhole = { git = "https://github.com/wormhole-foundation/wormhole", tag = "v2.17.1" }
sha3 = { version = "0.10.4" }
structopt = { version = "0.3.26" }
clap = { version = "4.4.4", features = ["derive", "env", "cargo"] }
strum = { version = "0.24.1", features = ["derive"] }
tokio = { version = "1.26.0", features = ["full"] }
tower-http = { version = "0.4.0", features = ["cors"] }

View File

@ -1,98 +1,58 @@
use {
libp2p::Multiaddr,
reqwest::Url,
solana_sdk::pubkey::Pubkey,
std::net::SocketAddr,
structopt::StructOpt,
use clap::{
crate_authors,
crate_description,
crate_name,
crate_version,
Args,
Parser,
};
const DEFAULT_NETWORK_ID: &str = "/wormhole/mainnet/2";
const DEFAULT_WORMHOLE_BOOTSTRAP_ADDRS: &str = "/dns4/wormhole-mainnet-v2-bootstrap.certus.one/udp/8999/quic/p2p/12D3KooWQp644DK27fd3d4Km3jr7gHiuJJ5ZGmy8hH4py7fP4FP7,/dns4/wormhole-v2-mainnet-bootstrap.xlabs.xyz/udp/8999/quic/p2p/12D3KooWNQ9tVrcb64tw6bNs2CaNrUGPM7yRrKvBBheQ5yCyPHKC";
const DEFAULT_WORMHOLE_LISTEN_ADDRS: &str = "/ip4/0.0.0.0/udp/30910/quic,/ip6/::/udp/30910/quic";
const DEFAULT_API_ADDR: &str = "127.0.0.1:33999";
mod benchmarks;
mod pythnet;
mod rpc;
mod wormhole;
/// `Options` is a structup definition to provide clean command-line args for Hermes.
#[derive(StructOpt, Debug)]
#[structopt(name = "hermes", about = "Hermes")]
// `Options` is a structup definition to provide clean command-line args for Hermes.
#[derive(Parser, Debug)]
#[command(name = crate_name!())]
#[command(author = crate_authors!())]
#[command(about = crate_description!())]
#[command(version = crate_version!())]
#[allow(clippy::large_enum_variant)]
pub enum Options {
/// Run the hermes service.
/// Run the Hermes Price Service.
Run(RunOptions),
/// Show Overridden Environment Variables.
ShowEnv(ShowEnvOptions),
}
#[derive(Clone, Debug, StructOpt)]
#[derive(Args, Clone, Debug)]
pub struct RunOptions {
/// Wormhole Options.
#[structopt(flatten)]
pub wormhole: WormholeOptions,
#[command(flatten)]
pub wormhole: wormhole::Options,
/// PythNet Options
#[structopt(flatten)]
pub pythnet: PythNetOptions,
#[command(flatten)]
pub pythnet: pythnet::Options,
/// RPC Options
#[structopt(flatten)]
pub rpc: RpcOptions,
#[command(flatten)]
pub rpc: rpc::Options,
/// Benchmarks Options
#[structopt(flatten)]
pub benchmarks: BenchmarksOptions,
#[command(flatten)]
pub benchmarks: benchmarks::Options,
}
#[derive(Clone, Debug, StructOpt)]
pub struct WormholeOptions {
/// Multiaddresses for Wormhole bootstrap peers (separated by comma).
#[structopt(long)]
#[structopt(use_delimiter = true)]
#[structopt(default_value = DEFAULT_WORMHOLE_BOOTSTRAP_ADDRS)]
#[structopt(env = "WORMHOLE_BOOTSTRAP_ADDRS")]
pub bootstrap_addrs: Vec<Multiaddr>,
/// Address of the Wormhole contract on the target PythNet cluster.
#[structopt(long)]
#[structopt(default_value = "H3fxXJ86ADW2PNuDDmZJg6mzTtPxkYCpNuQUTgmJ7AjU")]
#[structopt(env = "WORMHOLE_CONTRACT_ADDR")]
pub contract_addr: Pubkey,
/// Multiaddresses to bind Wormhole P2P to (separated by comma)
#[structopt(long)]
#[structopt(use_delimiter = true)]
#[structopt(default_value = DEFAULT_WORMHOLE_LISTEN_ADDRS)]
#[structopt(env = "WORMHOLE_LISTEN_ADDRS")]
pub listen_addrs: Vec<Multiaddr>,
/// Network ID for Wormhole
#[structopt(long)]
#[structopt(default_value = DEFAULT_NETWORK_ID)]
#[structopt(env = "WORMHOLE_NETWORK_ID")]
pub network_id: String,
}
#[derive(Clone, Debug, StructOpt)]
pub struct PythNetOptions {
/// Address of a PythNet compatible websocket RPC endpoint.
#[structopt(long)]
#[structopt(env = "PYTHNET_WS_ENDPOINT")]
pub ws_endpoint: String,
/// Addres of a PythNet compatible HTP RPC endpoint.
#[structopt(long)]
#[structopt(env = "PYTHNET_HTTP_ENDPOINT")]
pub http_endpoint: String,
}
#[derive(Clone, Debug, StructOpt)]
pub struct RpcOptions {
/// Address to bind the API server to.
#[structopt(long)]
#[structopt(default_value = DEFAULT_API_ADDR)]
#[structopt(env = "API_ADDR")]
pub addr: SocketAddr,
}
#[derive(Clone, Debug, StructOpt)]
pub struct BenchmarksOptions {
/// Benchmarks endpoint to retrieve historical update data from.
#[structopt(long)]
#[structopt(env = "BENCHMARKS_ENDPOINT")]
pub endpoint: Option<Url>,
#[derive(Args, Clone, Debug)]
pub struct ShowEnvOptions {
/// Show Hermes environment variables.
///
/// By default this command will attempt to read the variable from the environment and fall
/// back to the argument default if not present. Set this flag if you want only the defaults
/// and to ignore the current environment.
#[arg(long = "defaults")]
pub defaults: bool,
}

View File

@ -0,0 +1,14 @@
use {
clap::Args,
reqwest::Url,
};
#[derive(Args, Clone, Debug)]
#[command(next_help_heading = "Benchmark Options")]
#[group(id = "Benchmarks")]
pub struct Options {
/// Benchmarks endpoint to retrieve historical update data from.
#[arg(long = "benchmarks-endpoint")]
#[arg(env = "BENCHMARKS_ENDPOINT")]
pub endpoint: Option<Url>,
}

View File

@ -0,0 +1,16 @@
use clap::Args;
#[derive(Args, Clone, Debug)]
#[command(next_help_heading = "Pythnet Options")]
#[group(id = "Pythnet")]
pub struct Options {
/// Address of a PythNet compatible websocket RPC endpoint.
#[arg(long = "pythnet-ws-addr")]
#[arg(env = "PYTHNET_WS_ENDPOINT")]
pub ws_endpoint: String,
/// Addres of a PythNet compatible HTP RPC endpoint.
#[arg(long = "pythnet-http-addr")]
#[arg(env = "PYTHNET_HTTP_ENDPOINT")]
pub http_endpoint: String,
}

17
hermes/src/config/rpc.rs Normal file
View File

@ -0,0 +1,17 @@
use {
clap::Args,
std::net::SocketAddr,
};
const DEFAULT_RPC_ADDR: &str = "127.0.0.1:33999";
#[derive(Args, Clone, Debug)]
#[command(next_help_heading = "RPC Options")]
#[group(id = "RPC")]
pub struct Options {
/// Address and port the RPC server will bind to.
#[arg(long = "rpc-listen-addr")]
#[arg(default_value = DEFAULT_RPC_ADDR)]
#[arg(env = "RPC_ADDR")]
pub addr: SocketAddr,
}

View File

@ -0,0 +1,48 @@
use {
clap::Args,
libp2p::Multiaddr,
solana_sdk::pubkey::Pubkey,
};
const DEFAULT_LISTEN_ADDRS: &str = "/ip4/0.0.0.0/udp/30910/quic,/ip6/::/udp/30910/quic";
const DEFAULT_CONTRACT_ADDR: &str = "H3fxXJ86ADW2PNuDDmZJg6mzTtPxkYCpNuQUTgmJ7AjU";
const DEFAULT_NETWORK_ID: &str = "/wormhole/mainnet/2";
const DEFAULT_BOOTSTRAP_ADDRS: &str = concat![
"/dns4/wormhole-mainnet-v2-bootstrap.certus.one/udp/8999/quic/p2p/12D3KooWQp644DK27fd3d4Km3jr7gHiuJJ5ZGmy8hH4py7fP4FP7,",
"/dns4/wormhole-v2-mainnet-bootstrap.xlabs.xyz/udp/8999/quic/p2p/12D3KooWNQ9tVrcb64tw6bNs2CaNrUGPM7yRrKvBBheQ5yCyPHKC",
];
#[derive(Args, Clone, Debug)]
#[command(next_help_heading = "Wormhole Options")]
#[group(id = "Wormhole")]
pub struct Options {
/// Multiaddresses for Wormhole bootstrap peers (separated by comma).
///
/// Bootstraps can be found from the official Wormhole repository, note that these addresses
/// are only used to bootstrap peer discovery and are not necessarily used for data transfer.
/// Adding more peers will speed up P2P peer discovery.
#[arg(long = "wormhole-bootstrap-addrs")]
#[arg(value_delimiter = ',')]
#[arg(default_value = DEFAULT_BOOTSTRAP_ADDRS)]
#[arg(env = "WORMHOLE_BOOTSTRAP_ADDRS")]
pub bootstrap_addrs: Vec<Multiaddr>,
/// Address of the Wormhole contract on the target PythNet cluster.
#[arg(long = "wormhole-contract-addr")]
#[arg(default_value = DEFAULT_CONTRACT_ADDR)]
#[arg(env = "WORMHOLE_CONTRACT_ADDR")]
pub contract_addr: Pubkey,
/// Multiaddresses to bind for Wormhole P2P (separated by comma)
#[arg(long = "wormhole-listen-addrs")]
#[arg(value_delimiter = ',')]
#[arg(default_value = DEFAULT_LISTEN_ADDRS)]
#[arg(env = "WORMHOLE_LISTEN_ADDRS")]
pub listen_addrs: Vec<Multiaddr>,
/// Network ID for Wormhole
#[arg(long = "wormhole-network-id")]
#[arg(default_value = DEFAULT_NETWORK_ID)]
#[arg(env = "WORMHOLE_NETWORK_ID")]
pub network_id: String,
}

View File

@ -4,12 +4,15 @@
use {
crate::state::State,
anyhow::Result,
clap::{
CommandFactory,
Parser,
},
futures::future::join_all,
std::{
io::IsTerminal,
sync::atomic::AtomicBool,
},
structopt::StructOpt,
tokio::spawn,
};
@ -37,7 +40,7 @@ async fn init() -> Result<()> {
// Parse the command line arguments with StructOpt, will exit automatically on `--help` or
// with invalid arguments.
match config::Options::from_args() {
match config::Options::parse() {
config::Options::Run(opts) => {
tracing::info!("Starting hermes service...");
@ -68,6 +71,33 @@ async fn init() -> Result<()> {
task??;
}
}
config::Options::ShowEnv(opts) => {
// For each subcommand, scan for arguments that allow overriding with an ENV variable
// and print that variable.
for subcommand in config::Options::command().get_subcommands() {
for arg in subcommand.get_arguments() {
if let Some(env) = arg.get_env().and_then(|env| env.to_str()) {
// Find the defaults for this argument, if present.
let defaults = arg
.get_default_values()
.iter()
.map(|v| v.to_str().unwrap())
.collect::<Vec<_>>()
.join(",");
println!(
"{}={}",
env,
match opts.defaults {
true => defaults,
false => std::env::var(env).unwrap_or(defaults),
}
);
}
}
}
}
}
Ok(())