Skip to content

Commit 138efc3

Browse files
feat: coloured output
using --no-color flag to disable colours fixed a bug with duration formatting
1 parent a701298 commit 138efc3

File tree

6 files changed

+114
-23
lines changed

6 files changed

+114
-23
lines changed

Cargo.lock

+1-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "practicestuff"
3-
version = "0.3.3"
3+
version = "0.3.4"
44
description = "Simple CLI trainer for improving calculation and memorisation skills"
55
authors = ["Bartłomiej Jaszczak"]
66
repository = "https://gitlab.com/bartekjaszczak/practicestuff"

src/application.rs

+52-18
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,11 @@ use std::io::{self, Write};
33
use std::process;
44
use std::sync::Arc;
55

6+
use crossterm::style::Color;
67
use rand::Rng;
78

89
use crate::args::prelude::*;
10+
use crate::colour;
911
use crate::config::{BehaviourOnError, Config, NumberOfQuestions};
1012
use crate::question::{Generator, Question};
1113
use crate::skill::doomsday_algorithm;
@@ -142,8 +144,15 @@ impl AppImpl {
142144
}
143145

144146
fn handle_question(&self, question: &Question) {
145-
println!("\nQ: {}", question.prompt());
146-
print!("A: ");
147+
println!(
148+
"\n{}{}",
149+
colour::format_text("Q: ", self.use_colour(), Color::DarkYellow),
150+
question.prompt()
151+
);
152+
print!(
153+
"{}",
154+
colour::format_text("A: ", self.use_colour(), Color::DarkYellow)
155+
);
147156
io::stdout().flush().expect("IO operation failed (flush)");
148157

149158
self.stats.start_new_question();
@@ -190,25 +199,38 @@ impl AppImpl {
190199
}
191200
};
192201

193-
println!("{number_of_questions}. Use Ctrl+C to exit.");
202+
let number_of_questions =
203+
colour::format_text(number_of_questions, self.use_colour(), Color::DarkYellow);
204+
let ctrl_c = colour::format_text("Ctrl+C", self.use_colour(), Color::Yellow);
205+
206+
println!("{number_of_questions}. Use {ctrl_c} to exit.");
194207
}
195208

196209
fn print_stats_in_between(&self) {
197-
match self.number_of_questions() {
198-
NumberOfQuestions::Infinite => println!(
199-
"Time taken: {}, current accuracy: {} ({})",
200-
self.stats.get_last_question_time(),
201-
self.stats.get_current_accuracy(),
202-
self.stats.get_number_of_correct_answers(),
210+
let text = match self.number_of_questions() {
211+
NumberOfQuestions::Infinite => colour::format_text(
212+
&format!(
213+
"Time taken: {}, current accuracy: {} ({})",
214+
self.stats.get_last_question_time(),
215+
self.stats.get_current_accuracy(),
216+
self.stats.get_number_of_correct_answers()
217+
),
218+
self.use_colour(),
219+
Color::Grey,
203220
),
204-
NumberOfQuestions::Limited(_) => println!(
205-
"Time taken: {}, current accuracy: {} ({}), questions left: {}",
206-
self.stats.get_last_question_time(),
207-
self.stats.get_current_accuracy(),
208-
self.stats.get_number_of_correct_answers(),
209-
self.stats.get_number_of_remaining_questions(),
221+
NumberOfQuestions::Limited(_) => colour::format_text(
222+
&format!(
223+
"Time taken: {}, current accuracy: {} ({}), questions left: {}",
224+
self.stats.get_last_question_time(),
225+
self.stats.get_current_accuracy(),
226+
self.stats.get_number_of_correct_answers(),
227+
self.stats.get_number_of_remaining_questions(),
228+
),
229+
self.use_colour(),
230+
Color::Grey,
210231
),
211-
}
232+
};
233+
println!("{text}");
212234
}
213235

214236
fn print_stats_post_game(&self) {
@@ -251,9 +273,17 @@ impl AppImpl {
251273
fn print_answer_feedback(&self, correct: bool, correct_answer: &str) {
252274
let mut feedback = String::new();
253275
if correct {
254-
feedback.push_str(&Self::random_feedback_correct());
276+
feedback.push_str(&colour::format_text(
277+
&Self::random_feedback_correct(),
278+
self.use_colour(),
279+
Color::Green,
280+
));
255281
} else {
256-
feedback.push_str(&Self::random_feedback_incorrect());
282+
feedback.push_str(&colour::format_text(
283+
&Self::random_feedback_incorrect(),
284+
self.use_colour(),
285+
Color::Red,
286+
));
257287
match self.config.options.behaviour_on_error {
258288
BehaviourOnError::ShowCorrect => {
259289
feedback.push_str(&format!(" Correct answer: {correct_answer}"));
@@ -388,4 +418,8 @@ impl AppImpl {
388418
fn number_of_questions(&self) -> NumberOfQuestions {
389419
self.config.options.number_of_questions
390420
}
421+
422+
fn use_colour(&self) -> bool {
423+
self.config.options.use_colour
424+
}
391425
}

src/colour.rs

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
use crossterm::style::{Color, Stylize};
2+
3+
pub fn format_text(text: &str, apply: bool, colour: Color) -> String {
4+
if apply {
5+
text.with(colour).to_string()
6+
} else {
7+
text.to_string()
8+
}
9+
}

src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
mod application;
22
mod args;
3+
mod colour;
34
mod config;
45
mod question;
56
mod skill;

src/stats.rs

+50-3
Original file line numberDiff line numberDiff line change
@@ -231,8 +231,8 @@ impl Stats {
231231
let mut time = String::new();
232232
if hours > 0 {
233233
time.push_str(&format!("{hours}h "));
234-
}
235-
if minutes > 0 {
234+
time.push_str(&format!("{minutes}m "));
235+
} else if minutes > 0 {
236236
time.push_str(&format!("{minutes}m "));
237237
}
238238
time.push_str(&format!("{seconds}.{milliseconds}s"));
@@ -241,7 +241,7 @@ impl Stats {
241241
}
242242

243243
fn truncate_trailing_zeros(number: u32) -> String {
244-
let mut number = number.to_string();
244+
let mut number = format!("{number:0>3}");
245245
while number.ends_with('0') && number.len() > 1 {
246246
number.pop();
247247
}
@@ -487,4 +487,51 @@ mod tests {
487487
assert_ne!(min_time, "0.0s", "Should be **more or less** 0.05s");
488488
assert_ne!(avg_time, "0.0s", "Should be **more or less** 0.225s");
489489
}
490+
491+
#[test]
492+
fn duration_format() {
493+
assert_eq!(Stats::format_duration(&Duration::from_millis(0)), "0.0s");
494+
assert_eq!(Stats::format_duration(&Duration::from_millis(1)), "0.001s");
495+
assert_eq!(Stats::format_duration(&Duration::from_millis(10)), "0.01s");
496+
assert_eq!(Stats::format_duration(&Duration::from_millis(11)), "0.011s");
497+
assert_eq!(Stats::format_duration(&Duration::from_millis(100)), "0.1s");
498+
assert_eq!(
499+
Stats::format_duration(&Duration::from_millis(101)),
500+
"0.101s"
501+
);
502+
assert_eq!(Stats::format_duration(&Duration::from_millis(110)), "0.11s");
503+
assert_eq!(
504+
Stats::format_duration(&Duration::from_millis(111)),
505+
"0.111s"
506+
);
507+
assert_eq!(Stats::format_duration(&Duration::from_millis(1000)), "1.0s");
508+
assert_eq!(
509+
Stats::format_duration(&Duration::from_millis(1001)),
510+
"1.001s"
511+
);
512+
assert_eq!(
513+
Stats::format_duration(&Duration::from_millis(10 * 1000)),
514+
"10.0s"
515+
);
516+
assert_eq!(
517+
Stats::format_duration(&Duration::from_millis(60 * 1000)),
518+
"1m 0.0s"
519+
);
520+
assert_eq!(
521+
Stats::format_duration(&Duration::from_millis(10 * 60 * 1000)),
522+
"10m 0.0s"
523+
);
524+
assert_eq!(
525+
Stats::format_duration(&Duration::from_millis(60 * 60 * 1000)),
526+
"1h 0m 0.0s"
527+
);
528+
assert_eq!(
529+
Stats::format_duration(&Duration::from_millis(168 * 60 * 60 * 1000)),
530+
"168h 0m 0.0s"
531+
);
532+
assert_eq!(
533+
Stats::format_duration(&Duration::from_millis(3 * 60 * 60 * 1000 + 5 * 60 * 1000 + 7 * 1000 + 93)),
534+
"3h 5m 7.093s"
535+
);
536+
}
490537
}

0 commit comments

Comments
 (0)