From f603378f8b87ce1a5d6970b0873ed443d8f7741a Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Mon, 22 Nov 2021 15:47:34 -0600 Subject: [PATCH] Add crate docs for solana-cli-config (#21227) * Add crate docs for solana-cli-config * Clarify that keypair_path is actually a signing source --- Cargo.lock | 5 +-- cli-config/Cargo.toml | 3 ++ cli-config/src/config.rs | 68 +++++++++++++++++++++++++++++++++++ cli-config/src/lib.rs | 76 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 150 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index fcf3b20a5..49b6f6784 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -70,9 +70,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.44" +version = "1.0.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61604a8f862e1d5c3229fdd78f8b02c68dcf73a4c4b05fd636d12240aaa242c1" +checksum = "ee10e43ae4a853c0a3591d4e2ada1719e553be18199d9da9d4a83f5927c2f5c7" [[package]] name = "arc-swap" @@ -4616,6 +4616,7 @@ dependencies = [ name = "solana-cli-config" version = "1.9.0" dependencies = [ + "anyhow", "dirs-next", "lazy_static", "serde", diff --git a/cli-config/Cargo.toml b/cli-config/Cargo.toml index 22dab9bfe..8fd8bd784 100644 --- a/cli-config/Cargo.toml +++ b/cli-config/Cargo.toml @@ -17,5 +17,8 @@ serde_derive = "1.0.103" serde_yaml = "0.8.21" url = "2.2.2" +[dev-dependencies] +anyhow = "1.0.45" + [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] diff --git a/cli-config/src/config.rs b/cli-config/src/config.rs index d9706ef92..0f37e3455 100644 --- a/cli-config/src/config.rs +++ b/cli-config/src/config.rs @@ -4,6 +4,16 @@ use std::{collections::HashMap, io, path::Path}; use url::Url; lazy_static! { + /// The default path to the CLI configuration file. + /// + /// This is a [lazy_static] of `Option`, the value of which is + /// + /// > `~/.config/solana/cli/config.yml` + /// + /// It will only be `None` if it is unable to identify the user's home + /// directory, which should not happen under typical OS environments. + /// + /// [lazy_static]: https://docs.rs/lazy_static pub static ref CONFIG_FILE: Option = { dirs_next::home_dir().map(|mut path| { path.extend(&[".config", "solana", "cli", "config.yml"]); @@ -12,13 +22,44 @@ lazy_static! { }; } +/// The Solana CLI configuration. #[derive(Serialize, Deserialize, Debug, PartialEq)] pub struct Config { + /// The RPC address of a Solana validator node. + /// + /// Typical values for mainnet, devnet, and testnet are [described in the + /// Solana documentation][rpcdocs]. + /// + /// For local testing, the typical value is `http://localhost:8899`. + /// + /// [rpcdocs]: https://docs.solana.com/cluster/rpc-endpoints pub json_rpc_url: String, + /// The address to connect to for receiving event notifications. + /// + /// If it is an empty string then the correct value will be derived + /// from `json_rpc_url`. + /// + /// The default value is the empty string. pub websocket_url: String, + /// The default signing source, which may be a keypair file, but may also + /// represent several other types of signers, as described in the + /// documentation for `solana_clap_utils::keypair::signer_from_path`. + /// Because it represents sources other than a simple path, the name + /// `keypair_path` is misleading, and exists for backwards compatibility + /// reasons. + /// + /// The signing source can be loaded with either the `signer_from_path` + /// function, or with `solana_clap_utils::keypair::DefaultSigner`. pub keypair_path: String, + /// A mapping from Solana addresses to human-readable names. + /// + /// By default the only value in this map is the system program. #[serde(default)] pub address_labels: HashMap, + /// The default commitment level. + /// + /// By default the value is "confirmed", as defined by + /// `solana_sdk::commitment_config::CommitmentLevel::Confirmed`. #[serde(default)] pub commitment: String, } @@ -55,14 +96,37 @@ impl Default for Config { } impl Config { + /// Load a configuration from file. + /// + /// # Errors + /// + /// This function may return typical file I/O errors. pub fn load(config_file: &str) -> Result { crate::load_config_file(config_file) } + /// Save a configuration to file. + /// + /// If the file's directory does not exist, it will be created. If the file + /// already exists, it will be overwritten. + /// + /// # Errors + /// + /// This function may return typical file I/O errors. pub fn save(&self, config_file: &str) -> Result<(), io::Error> { crate::save_config_file(self, config_file) } + /// Compute the websocket URL from the RPC URL. + /// + /// The address is created from the RPC URL by: + /// + /// - adding 1 to the port number, + /// - using the "wss" scheme if the RPC URL has an "https" scheme, or the + /// "ws" scheme if the RPC URL has an "http" scheme. + /// + /// If `json_rpc_url` cannot be parsed as a URL then this function returns + /// the empty string. pub fn compute_websocket_url(json_rpc_url: &str) -> String { let json_rpc_url: Option = json_rpc_url.parse().ok(); if json_rpc_url.is_none() { @@ -81,6 +145,8 @@ impl Config { ws_url.to_string() } + /// Load a map of address/name pairs from a YAML file at the given path and + /// insert them into the configuration. pub fn import_address_labels

(&mut self, filename: P) -> Result<(), io::Error> where P: AsRef, @@ -92,6 +158,8 @@ impl Config { Ok(()) } + /// Save the map of address/name pairs contained in the configuration to a + /// YAML file at the given path. pub fn export_address_labels

(&self, filename: P) -> Result<(), io::Error> where P: AsRef, diff --git a/cli-config/src/lib.rs b/cli-config/src/lib.rs index 435e9762c..ea0f46404 100644 --- a/cli-config/src/lib.rs +++ b/cli-config/src/lib.rs @@ -1,3 +1,56 @@ +//! Loading and saving the Solana CLI configuration file. +//! +//! The configuration file used by the Solana CLI includes information about the +//! RPC node to connect to, the path to the user's signing source, and more. +//! Other software than the Solana CLI may wish to access the same configuration +//! and signer. +//! +//! The default path to the configuration file can be retrieved from +//! [`CONFIG_FILE`], which is a [lazy_static] of `Option`, the value of +//! which is +//! +//! > `~/.config/solana/cli/config.yml` +//! +//! [`CONFIG_FILE`]: struct@CONFIG_FILE +//! [lazy_static]: https://docs.rs/lazy_static +//! +//! `CONFIG_FILE` will only be `None` if it is unable to identify the user's +//! home directory, which should not happen under typical OS environments. +//! +//! The CLI configuration is defined by the [`Config`] struct, and its value is +//! loaded with [`Config::load`] and saved with [`Config::save`]. +//! +//! Two important fields of `Config` are +//! +//! - [`json_rpc_url`], the URL to pass to +//! `solana_client::rpc_client::RpcClient`. +//! - [`keypair_path`], a signing source, which may be a keypair file, but +//! may also represent several other types of signers, as described in +//! the documentation for `solana_clap_utils::keypair::signer_from_path`. +//! +//! [`json_rpc_url`]: Config::json_rpc_url +//! [`keypair_path`]: Config::keypair_path +//! +//! # Examples +//! +//! Loading and saving the configuration. Note that this uses the [anyhow] crate +//! for error handling. +//! +//! [anyhow]: https://docs.rs/anyhow +//! +//! ```no_run +//! use anyhow::anyhow; +//! use solana_cli_config::{CONFIG_FILE, Config}; +//! +//! let config_file = solana_cli_config::CONFIG_FILE.as_ref() +//! .ok_or_else(|| anyhow!("unable to get config file path"))?; +//! let mut cli_config = Config::load(&config_file)?; +//! // Set the RPC URL to devnet +//! cli_config.json_rpc_url = "https://api.devnet.solana.com".to_string(); +//! cli_config.save(&config_file)?; +//! # Ok::<(), anyhow::Error>(()) +//! ``` + #[macro_use] extern crate lazy_static; @@ -10,6 +63,16 @@ use std::{ path::Path, }; +/// Load a value from a file in YAML format. +/// +/// Despite the name, this function is generic YAML file deserializer, a thin +/// wrapper around serde. +/// +/// Most callers should instead use [`Config::load`]. +/// +/// # Errors +/// +/// This function may return typical file I/O errors. pub fn load_config_file(config_file: P) -> Result where T: serde::de::DeserializeOwned, @@ -21,6 +84,19 @@ where Ok(config) } +/// Save a value to a file in YAML format. +/// +/// Despite the name, this function is a generic YAML file serializer, a thin +/// wrapper around serde. +/// +/// If the file's directory does not exist, it will be created. If the file +/// already exists, it will be overwritten. +/// +/// Most callers should instead use [`Config::save`]. +/// +/// # Errors +/// +/// This function may return typical file I/O errors. pub fn save_config_file(config: &T, config_file: P) -> Result<(), io::Error> where T: serde::ser::Serialize,