diff --git a/Cargo.lock b/Cargo.lock index e60e34613..e29bd2c2b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4700,6 +4700,7 @@ dependencies = [ "futures", "futures-util", "gix", + "glob", "heck 0.5.0", "hex", "home", diff --git a/FULL_HELP_DOCS.md b/FULL_HELP_DOCS.md index 57a4ef692..b7480cb5e 100644 --- a/FULL_HELP_DOCS.md +++ b/FULL_HELP_DOCS.md @@ -163,6 +163,7 @@ Utilities to manage contract aliases * `remove` — Remove contract alias * `add` — Add contract alias * `show` — Show the contract id associated with a given alias +* `ls` — List all aliases @@ -228,6 +229,19 @@ Show the contract id associated with a given alias +## `stellar contract alias ls` + +List all aliases + +**Usage:** `stellar contract alias ls [OPTIONS]` + +###### **Options:** + +* `--global` — Use global config +* `--config-dir ` — Location of config directory, default is "." + + + ## `stellar contract bindings` Generate code client bindings for a contract diff --git a/cmd/soroban-cli/Cargo.toml b/cmd/soroban-cli/Cargo.toml index 3ffb7466e..3d88d9182 100644 --- a/cmd/soroban-cli/Cargo.toml +++ b/cmd/soroban-cli/Cargo.toml @@ -121,6 +121,7 @@ flate2 = "1.0.30" bytesize = "1.3.0" humantime = "2.1.0" phf = { version = "0.11.2", features = ["macros"] } +glob = "0.3.1" # For hyper-tls [target.'cfg(unix)'.dependencies] diff --git a/cmd/soroban-cli/src/commands/contract/alias.rs b/cmd/soroban-cli/src/commands/contract/alias.rs index bea808cb4..7d9213097 100644 --- a/cmd/soroban-cli/src/commands/contract/alias.rs +++ b/cmd/soroban-cli/src/commands/contract/alias.rs @@ -1,6 +1,7 @@ use crate::commands::global; pub mod add; +pub mod ls; pub mod remove; pub mod show; @@ -14,6 +15,9 @@ pub enum Cmd { /// Show the contract id associated with a given alias Show(show::Cmd), + + /// List all aliases + Ls(ls::Cmd), } #[derive(thiserror::Error, Debug)] @@ -26,6 +30,9 @@ pub enum Error { #[error(transparent)] Show(#[from] show::Error), + + #[error(transparent)] + Ls(#[from] ls::Error), } impl Cmd { @@ -34,6 +41,7 @@ impl Cmd { Cmd::Remove(remove) => remove.run(global_args)?, Cmd::Add(add) => add.run(global_args)?, Cmd::Show(show) => show.run(global_args)?, + Cmd::Ls(ls) => ls.run()?, } Ok(()) } diff --git a/cmd/soroban-cli/src/commands/contract/alias/ls.rs b/cmd/soroban-cli/src/commands/contract/alias/ls.rs new file mode 100644 index 000000000..8d59ff2c5 --- /dev/null +++ b/cmd/soroban-cli/src/commands/contract/alias/ls.rs @@ -0,0 +1,104 @@ +use std::collections::HashMap; +use std::fmt::Debug; +use std::{fs, process}; + +use clap::{command, Parser}; + +use crate::commands::config::network; +use crate::config::{alias, locator}; + +#[derive(Parser, Debug, Clone)] +#[group(skip)] +pub struct Cmd { + #[command(flatten)] + pub config_locator: locator::Args, +} + +#[derive(thiserror::Error, Debug)] +pub enum Error { + #[error(transparent)] + Locator(#[from] locator::Error), + + #[error(transparent)] + Network(#[from] network::Error), + + #[error(transparent)] + PatternError(#[from] glob::PatternError), + + #[error(transparent)] + GlobError(#[from] glob::GlobError), + + #[error(transparent)] + IoError(#[from] std::io::Error), +} + +#[derive(Debug, Clone)] +struct AliasEntry { + alias: String, + contract: String, +} + +impl Cmd { + pub fn run(&self) -> Result<(), Error> { + let config_dir = self.config_locator.config_dir()?; + let pattern = config_dir + .join("contract-ids") + .join("*.json") + .to_string_lossy() + .into_owned(); + + let paths = glob::glob(&pattern)?; + let mut found = false; + let mut map: HashMap> = HashMap::new(); + + for path in paths { + let path = path?; + + if let Some(alias) = path.file_stem() { + let alias = alias.to_string_lossy().into_owned(); + let content = fs::read_to_string(path)?; + let data: alias::Data = serde_json::from_str(&content).unwrap_or_default(); + + for network_passphrase in data.ids.keys() { + let network_passphrase = network_passphrase.to_string(); + let contract = data + .ids + .get(&network_passphrase) + .map(ToString::to_string) + .unwrap_or_default(); + let entry = AliasEntry { + alias: alias.clone(), + contract, + }; + + let list = map.entry(network_passphrase.clone()).or_default(); + + list.push(entry.clone()); + } + } + } + + for network_passphrase in map.keys() { + if let Some(list) = map.clone().get_mut(network_passphrase) { + println!("ℹ️ Aliases available for network '{network_passphrase}'"); + + list.sort_by(|a, b| a.alias.cmp(&b.alias)); + + for entry in list { + found = true; + println!("{}: {}", entry.alias, entry.contract); + } + + println!(); + } + } + + if !found { + eprintln!("⚠️ No aliases defined for network"); + + process::exit(1); + } + + Ok(()) + } +}