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 pub fn print<T: Display + Sized>(&self, message: T) {
22 if !self.quiet {
23 eprint!("{message}");
24 }
25 }
26
27 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 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 pub fn log_explorer_url(&self, network: &Network, tx_hash: &str) {
61 if let Some(url) = explorer_url_for_transaction(network, tx_hash) {
62 self.linkln(url);
63 }
64 }
65
66 pub fn log_transaction(
70 &self,
71 tx: &Transaction,
72 network: &Network,
73 show_link: bool,
74 ) -> Result<(), XdrError> {
75 let tx_hash = transaction_hash(tx, &network.network_passphrase)?;
76 let hash = hex::encode(tx_hash);
77
78 self.infoln(format!("Transaction hash is {hash}").as_str());
79
80 if show_link {
81 self.log_explorer_url(network, &hash);
82 }
83
84 Ok(())
85 }
86}
87
88macro_rules! create_print_functions {
89 ($name:ident, $nameln:ident, $icon:expr) => {
90 impl Print {
91 #[allow(dead_code)]
92 pub fn $name<T: Display + Sized>(&self, message: T) {
93 if !self.quiet {
94 eprint!("{} {}", self.compute_emoji($icon), message);
95 }
96 }
97
98 #[allow(dead_code)]
99 pub fn $nameln<T: Display + Sized>(&self, message: T) {
100 if !self.quiet {
101 eprintln!("{} {}", self.compute_emoji($icon), message);
102 }
103 }
104 }
105 };
106}
107
108pub fn format_number<T: TryInto<i128>>(n: T, decimals: u32) -> String {
112 match n.try_into() {
113 Ok(value) => crate::fixed_point::FixedPoint::new(value, decimals).to_string(),
114 Err(_) => "Err(number out of bounds)".to_string(),
115 }
116}
117
118fn should_add_additional_space() -> bool {
119 const TERMS: &[&str] = &["Apple_Terminal", "vscode", "unknown"];
120 let term_program = env::var("TERM_PROGRAM").unwrap_or("unknown".to_string());
121
122 if TERMS.contains(&term_program.as_str()) {
123 return true;
124 }
125
126 false
127}
128
129create_print_functions!(bucket, bucketln, "đĒŖ");
130create_print_functions!(check, checkln, "â
");
131create_print_functions!(error, errorln, "â");
132create_print_functions!(globe, globeln, "đ");
133create_print_functions!(info, infoln, "âšī¸");
134create_print_functions!(link, linkln, "đ");
135create_print_functions!(plus, plusln, "â");
136create_print_functions!(save, saveln, "đž");
137create_print_functions!(search, searchln, "đ");
138create_print_functions!(warn, warnln, "â ī¸");
139create_print_functions!(exclaim, exclaimln, "âī¸");
140create_print_functions!(arrow, arrowln, "âĄī¸");
141create_print_functions!(log, logln, "đ");
142create_print_functions!(event, eventln, "đ
");
143create_print_functions!(blank, blankln, " ");
144create_print_functions!(gear, gearln, "âī¸");
145create_print_functions!(dir, dirln, "đ");
146
147#[cfg(test)]
148mod tests {
149 use super::*;
150
151 #[test]
152 #[allow(clippy::unreadable_literal)]
153 fn test_format_number() {
154 assert_eq!(format_number(0i128, 7), "0");
155 assert_eq!(format_number(1234567i128, 7), "0.1234567");
156 assert_eq!(format_number(12345000i128, 7), "1.2345");
157 assert_eq!(format_number(10000000i128, 7), "1");
158 assert_eq!(format_number(123456789012345i128, 7), "12345678.9012345");
159 assert_eq!(format_number(-1234567i128, 7), "-0.1234567");
160 assert_eq!(format_number(-12345000i128, 7), "-1.2345");
161 assert_eq!(format_number(12345i128, 0), "12345");
162 assert_eq!(format_number(12345i128, 1), "1234.5");
163 assert_eq!(format_number(1i128, 7), "0.0000001");
164
165 assert_eq!(format_number(1u32, 7), "0.0000001");
166 assert_eq!(format_number(1i32, 7), "0.0000001");
167 assert_eq!(format_number(1u64, 7), "0.0000001");
168 assert_eq!(format_number(1i64, 7), "0.0000001");
169 assert_eq!(format_number(1u128, 7), "0.0000001");
170
171 let err: u128 = u128::try_from(i128::MAX).unwrap() + 1;
172 let result = format_number(err, 0);
173 assert_eq!(result, "Err(number out of bounds)");
174
175 let min: i128 = i128::MIN;
176 let result = format_number(min, 18);
177 assert_eq!(result, "-170141183460469231731.687303715884105728");
178
179 let max: i128 = i128::MAX;
180 let result = format_number(max, 18);
181 assert_eq!(result, "170141183460469231731.687303715884105727");
182 }
183}