⭐️ A friendly language for building type-safe, scalable systems!
at main 5.7 kB view raw
1use gleam_core::{ 2 build::Telemetry, 3 error::{Error, StandardIoAction}, 4}; 5use hexpm::version::Version; 6use std::{ 7 io::{IsTerminal, Write}, 8 time::{Duration, Instant}, 9}; 10use termcolor::{BufferWriter, Color, ColorChoice, ColorSpec, WriteColor}; 11 12#[derive(Debug, Default, Clone)] 13pub struct Reporter; 14 15impl Reporter { 16 pub fn new() -> Self { 17 Self 18 } 19} 20 21impl Telemetry for Reporter { 22 fn compiled_package(&self, duration: Duration) { 23 print_compiled(duration); 24 } 25 26 fn compiling_package(&self, name: &str) { 27 print_compiling(name); 28 } 29 30 fn checked_package(&self, duration: Duration) { 31 print_checked(duration); 32 } 33 34 fn checking_package(&self, name: &str) { 35 print_checking(name); 36 } 37 38 fn downloading_package(&self, name: &str) { 39 print_downloading(name) 40 } 41 42 fn packages_downloaded(&self, start: Instant, count: usize) { 43 print_packages_downloaded(start, count) 44 } 45 46 fn resolving_package_versions(&self) { 47 print_resolving_versions() 48 } 49 50 fn running(&self, name: &str) { 51 print_running(name); 52 } 53 54 fn waiting_for_build_directory_lock(&self) { 55 print_waiting_for_build_directory_lock() 56 } 57} 58 59pub fn ask(question: &str) -> Result<String, Error> { 60 print!("{question}: "); 61 std::io::stdout().flush().expect("ask stdout flush"); 62 let mut answer = String::new(); 63 let _ = std::io::stdin() 64 .read_line(&mut answer) 65 .map_err(|e| Error::StandardIo { 66 action: StandardIoAction::Read, 67 err: Some(e.kind()), 68 })?; 69 Ok(answer.trim().to_string()) 70} 71 72pub fn confirm(question: &str) -> Result<bool, Error> { 73 let answer = ask(&format!("{question} [y/n]"))?; 74 match answer.as_str() { 75 "y" | "yes" | "Y" | "YES" => Ok(true), 76 _ => Ok(false), 77 } 78} 79 80pub fn confirm_with_text(response: &str) -> Result<bool, Error> { 81 let answer = ask(&format!("Type '{response}' to continue"))?; 82 Ok(response == answer) 83} 84 85pub fn ask_password(question: &str) -> Result<String, Error> { 86 let prompt = format!("{question} (will not be printed as you type): "); 87 rpassword::prompt_password(prompt) 88 .map_err(|e| Error::StandardIo { 89 action: StandardIoAction::Read, 90 err: Some(e.kind()), 91 }) 92 .map(|s| s.trim().to_string()) 93} 94 95pub fn print_publishing(name: &str, version: &Version) { 96 print_colourful_prefix("Publishing", &format!("{name} v{version}")) 97} 98 99pub fn print_published(duration: Duration) { 100 print_colourful_prefix("Published", &format!("in {}", seconds(duration))) 101} 102 103pub fn print_retired(package: &str, version: &str) { 104 print_colourful_prefix("Retired", &format!("{package} {version}")) 105} 106 107pub fn print_unretired(package: &str, version: &str) { 108 print_colourful_prefix("Unretired", &format!("{package} {version}")) 109} 110 111pub fn print_publishing_documentation() { 112 print_colourful_prefix("Publishing", "documentation"); 113} 114 115fn print_downloading(text: &str) { 116 print_colourful_prefix("Downloading", text) 117} 118 119fn print_waiting_for_build_directory_lock() { 120 print_colourful_prefix("Waiting", "for build directory lock") 121} 122 123fn print_resolving_versions() { 124 print_colourful_prefix("Resolving", "versions") 125} 126 127fn print_compiling(text: &str) { 128 print_colourful_prefix("Compiling", text) 129} 130 131pub(crate) fn print_exported(text: &str) { 132 print_colourful_prefix("Exported", text) 133} 134 135pub(crate) fn print_checking(text: &str) { 136 print_colourful_prefix("Checking", text) 137} 138 139pub(crate) fn print_compiled(duration: Duration) { 140 print_colourful_prefix("Compiled", &format!("in {}", seconds(duration))) 141} 142 143pub(crate) fn print_checked(duration: Duration) { 144 print_colourful_prefix("Checked", &format!("in {}", seconds(duration))) 145} 146 147pub(crate) fn print_running(text: &str) { 148 print_colourful_prefix("Running", text) 149} 150 151pub(crate) fn print_added(text: &str) { 152 print_colourful_prefix("Added", text) 153} 154 155pub(crate) fn print_removed(text: &str) { 156 print_colourful_prefix("Removed", text) 157} 158 159pub(crate) fn print_generating_documentation() { 160 print_colourful_prefix("Generating", "documentation") 161} 162 163fn print_packages_downloaded(start: Instant, count: usize) { 164 let elapsed = seconds(start.elapsed()); 165 let msg = match count { 166 1 => format!("1 package in {elapsed}"), 167 _ => format!("{count} packages in {elapsed}"), 168 }; 169 print_colourful_prefix("Downloaded", &msg) 170} 171 172pub fn seconds(duration: Duration) -> String { 173 format!("{:.2}s", duration.as_millis() as f32 / 1000.) 174} 175 176pub fn print_colourful_prefix(prefix: &str, text: &str) { 177 let buffer_writer = stderr_buffer_writer(); 178 let mut buffer = buffer_writer.buffer(); 179 buffer 180 .set_color( 181 ColorSpec::new() 182 .set_intense(true) 183 .set_fg(Some(Color::Magenta)), 184 ) 185 .expect("print_green_prefix"); 186 write!(buffer, "{prefix: >11}").expect("print_green_prefix"); 187 buffer 188 .set_color(&ColorSpec::new()) 189 .expect("print_green_prefix"); 190 writeln!(buffer, " {text}").expect("print_green_prefix"); 191 buffer_writer.print(&buffer).expect("print_green_prefix"); 192} 193 194pub fn stderr_buffer_writer() -> BufferWriter { 195 // Don't add color codes to the output if standard error isn't connected to a terminal 196 BufferWriter::stderr(color_choice()) 197} 198 199fn colour_forced() -> bool { 200 if let Ok(force) = std::env::var("FORCE_COLOR") { 201 !force.is_empty() 202 } else { 203 false 204 } 205} 206 207fn color_choice() -> ColorChoice { 208 if colour_forced() { 209 ColorChoice::Always 210 } else if std::io::stderr().is_terminal() { 211 ColorChoice::Auto 212 } else { 213 ColorChoice::Never 214 } 215}