Skip to main content

soroban_cli/commands/env/
mod.rs

1use crate::{
2    commands::global,
3    config::locator::{self},
4    env_vars,
5    print::Print,
6    utils::escape_control_characters,
7};
8use clap::Parser;
9use shell_escape::escape;
10
11#[allow(clippy::doc_markdown)]
12#[derive(Debug, Parser)]
13pub struct Cmd {
14    /// Env variable name to get the value of.
15    ///
16    /// E.g.: $ stellar env STELLAR_ACCOUNT
17    #[arg()]
18    pub name: Option<String>,
19
20    /// Whether to reveal the value of concealed env vars. By default, concealed env vars are
21    /// hidden behind a placeholder value.
22    #[arg(long)]
23    pub reveal: bool,
24
25    #[command(flatten)]
26    pub config_locator: locator::Args,
27}
28
29#[derive(thiserror::Error, Debug)]
30pub enum Error {
31    #[error(transparent)]
32    Locator(#[from] locator::Error),
33}
34
35impl Cmd {
36    pub fn run(&self, global_args: &global::Args) -> Result<(), Error> {
37        let print = Print::new(global_args.quiet);
38        let mut vars: Vec<EnvVar> = Vec::new();
39        let supported = env_vars::prefixed("STELLAR");
40
41        for key in supported {
42            if let Some(mut v) = EnvVar::get(&key) {
43                if self.reveal {
44                    v.reveal();
45                }
46
47                vars.push(v);
48            }
49        }
50
51        // If a specific name is given, just print that one value
52        if let Some(name) = &self.name {
53            if let Some(v) = vars.iter().find(|v| &v.key == name) {
54                if v.is_revealed() {
55                    println!("{}", escape_control_characters(&v.value));
56                }
57            }
58
59            return Ok(());
60        }
61
62        if vars.is_empty() {
63            print.warnln("No defaults or environment variables set".to_string());
64            return Ok(());
65        }
66
67        let max_len = vars.iter().map(|v| v.str().len()).max().unwrap_or(0);
68
69        vars.sort();
70
71        for v in vars {
72            println!("{:max_len$} # {}", v.str(), v.source);
73        }
74
75        Ok(())
76    }
77}
78
79#[derive(PartialEq, Eq, PartialOrd, Ord)]
80struct EnvVar {
81    key: String,
82    value: String,
83    source: String,
84    reveal: bool,
85}
86
87impl EnvVar {
88    fn get(key: &str) -> Option<Self> {
89        // The _SOURCE env var is set from cmd/soroban-cli/src/cli.rs#set_env_value_from_config
90        let source = std::env::var(format!("{key}_SOURCE"))
91            .ok()
92            .unwrap_or("env".to_string());
93
94        if let Ok(value) = std::env::var(key) {
95            return Some(EnvVar {
96                key: key.to_string(),
97                value,
98                source,
99                reveal: false,
100            });
101        }
102
103        None
104    }
105
106    fn reveal(&mut self) {
107        self.reveal = true;
108    }
109
110    fn is_revealed(&self) -> bool {
111        self.reveal || !env_vars::is_concealed(&self.key)
112    }
113
114    fn str(&self) -> String {
115        if self.is_revealed() {
116            let value = escape(escape_control_characters(&self.value).into());
117            format!("{}={value}", self.key)
118        } else {
119            format!("# {}=<concealed>", self.key)
120        }
121    }
122}