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