soroban_cli/commands/contract/alias/
ls.rs

1use clap::{command, Parser};
2use std::collections::HashMap;
3use std::fmt::Debug;
4use std::path::Path;
5use std::{fs, process};
6
7use crate::commands::config::network;
8use crate::config::locator::{print_deprecation_warning, Location};
9use crate::config::{alias, locator};
10
11#[derive(Parser, Debug, Clone)]
12#[group(skip)]
13pub struct Cmd {
14    #[command(flatten)]
15    pub config_locator: locator::Args,
16}
17
18#[derive(thiserror::Error, Debug)]
19pub enum Error {
20    #[error(transparent)]
21    Locator(#[from] locator::Error),
22
23    #[error(transparent)]
24    Network(#[from] network::Error),
25
26    #[error(transparent)]
27    PatternError(#[from] glob::PatternError),
28
29    #[error(transparent)]
30    GlobError(#[from] glob::GlobError),
31
32    #[error(transparent)]
33    IoError(#[from] std::io::Error),
34}
35
36#[derive(Debug, Clone)]
37struct AliasEntry {
38    alias: String,
39    contract: String,
40}
41
42impl Cmd {
43    pub fn run(&self) -> Result<(), Error> {
44        let config_dirs = self.config_locator.local_and_global()?;
45
46        for cfg in config_dirs {
47            match cfg {
48                Location::Local(config_dir) => Self::read_from_config_dir(&config_dir, true)?,
49                Location::Global(config_dir) => Self::read_from_config_dir(&config_dir, false)?,
50            }
51        }
52
53        Ok(())
54    }
55
56    fn read_from_config_dir(config_dir: &Path, deprecation_mode: bool) -> Result<(), Error> {
57        let pattern = config_dir
58            .join("contract-ids")
59            .join("*.json")
60            .to_string_lossy()
61            .into_owned();
62
63        let paths = glob::glob(&pattern)?;
64        let mut found = false;
65        let mut map: HashMap<String, Vec<AliasEntry>> = HashMap::new();
66
67        for path in paths {
68            let path = path?;
69
70            if let Some(alias) = path.file_stem() {
71                let alias = alias.to_string_lossy().into_owned();
72                let content = fs::read_to_string(path)?;
73                let data: alias::Data = serde_json::from_str(&content).unwrap_or_default();
74
75                for network_passphrase in data.ids.keys() {
76                    let network_passphrase = network_passphrase.clone();
77                    let contract = data
78                        .ids
79                        .get(&network_passphrase)
80                        .map(ToString::to_string)
81                        .unwrap_or_default();
82                    let entry = AliasEntry {
83                        alias: alias.clone(),
84                        contract,
85                    };
86
87                    let list = map.entry(network_passphrase.clone()).or_default();
88
89                    list.push(entry.clone());
90                }
91            }
92        }
93
94        for network_passphrase in map.keys() {
95            if let Some(list) = map.clone().get_mut(network_passphrase) {
96                println!("ℹ️ Aliases available for network '{network_passphrase}'");
97
98                list.sort_by(|a, b| a.alias.cmp(&b.alias));
99
100                for entry in list {
101                    if !found && deprecation_mode {
102                        print_deprecation_warning(config_dir);
103                    }
104                    found = true;
105                    println!("{}: {}", entry.alias, entry.contract);
106                }
107
108                println!();
109            }
110        }
111
112        if !found && !deprecation_mode {
113            eprintln!("⚠️ No aliases defined for network");
114
115            process::exit(1);
116        }
117
118        Ok(())
119    }
120}