Skip to main content

soroban_cli/
print.rs

1use std::io::{self, Write};
2use std::{env, fmt::Display};
3
4use crate::xdr::{Error as XdrError, Transaction};
5
6use crate::{
7    config::network::Network, utils::explorer_url_for_transaction, utils::transaction_hash,
8};
9
10#[derive(Clone)]
11pub struct Print {
12    pub quiet: bool,
13}
14
15impl Print {
16    pub fn new(quiet: bool) -> Print {
17        Print { quiet }
18    }
19
20    /// Print message to stderr if not in quiet mode
21    pub fn print<T: Display + Sized>(&self, message: T) {
22        if !self.quiet {
23            eprint!("{message}");
24        }
25    }
26
27    /// Print message with newline to stderr if not in quiet mode.
28    pub fn println<T: Display + Sized>(&self, message: T) {
29        if !self.quiet {
30            eprintln!("{message}");
31        }
32    }
33
34    pub fn clear_previous_line(&self) {
35        if !self.quiet {
36            if cfg!(windows) {
37                eprint!("\x1b[2A\r\x1b[2K");
38            } else {
39                eprint!("\x1b[1A\x1b[2K\r");
40            }
41
42            io::stderr().flush().unwrap();
43        }
44    }
45
46    // Some terminals like vscode's and macOS' default terminal will not render
47    // the subsequent space if the emoji codepoints size is 2; in this case,
48    // we need an additional space. We also need an additional space if `TERM_PROGRAM` is not
49    // defined (e.g. vhs running in a docker container).
50    pub fn compute_emoji<T: Display + Sized>(&self, emoji: T) -> String {
51        if should_add_additional_space()
52            && (emoji.to_string().chars().count() == 2 || format!("{emoji}") == " ")
53        {
54            return format!("{emoji} ");
55        }
56
57        emoji.to_string()
58    }
59
60    /// # Errors
61    ///
62    /// Might return an error
63    pub fn log_transaction(
64        &self,
65        tx: &Transaction,
66        network: &Network,
67        show_link: bool,
68    ) -> Result<(), XdrError> {
69        let tx_hash = transaction_hash(tx, &network.network_passphrase)?;
70        let hash = hex::encode(tx_hash);
71
72        self.infoln(format!("Transaction hash is {hash}").as_str());
73
74        if show_link {
75            if let Some(url) = explorer_url_for_transaction(network, &hash) {
76                self.linkln(url);
77            }
78        }
79
80        Ok(())
81    }
82}
83
84macro_rules! create_print_functions {
85    ($name:ident, $nameln:ident, $icon:expr) => {
86        impl Print {
87            #[allow(dead_code)]
88            pub fn $name<T: Display + Sized>(&self, message: T) {
89                if !self.quiet {
90                    eprint!("{} {}", self.compute_emoji($icon), message);
91                }
92            }
93
94            #[allow(dead_code)]
95            pub fn $nameln<T: Display + Sized>(&self, message: T) {
96                if !self.quiet {
97                    eprintln!("{} {}", self.compute_emoji($icon), message);
98                }
99            }
100        }
101    };
102}
103
104fn should_add_additional_space() -> bool {
105    const TERMS: &[&str] = &["Apple_Terminal", "vscode", "unknown"];
106    let term_program = env::var("TERM_PROGRAM").unwrap_or("unknown".to_string());
107
108    if TERMS.contains(&term_program.as_str()) {
109        return true;
110    }
111
112    false
113}
114
115create_print_functions!(bucket, bucketln, "đŸĒŖ");
116create_print_functions!(check, checkln, "✅");
117create_print_functions!(error, errorln, "❌");
118create_print_functions!(globe, globeln, "🌎");
119create_print_functions!(info, infoln, "â„šī¸");
120create_print_functions!(link, linkln, "🔗");
121create_print_functions!(plus, plusln, "➕");
122create_print_functions!(save, saveln, "💾");
123create_print_functions!(search, searchln, "🔎");
124create_print_functions!(warn, warnln, "âš ī¸");
125create_print_functions!(exclaim, exclaimln, "â—ī¸");
126create_print_functions!(arrow, arrowln, "âžĄī¸");
127create_print_functions!(log, logln, "📔");
128create_print_functions!(event, eventln, "📅");
129create_print_functions!(blank, blankln, "  ");
130create_print_functions!(gear, gearln, "âš™ī¸");
131create_print_functions!(dir, dirln, "📁");