mirror of
https://github.com/rust-lang/rustlings.git
synced 2024-12-25 00:00:05 +03:00
Compare commits
14 commits
8b0c67b397
...
b040d492cd
Author | SHA1 | Date | |
---|---|---|---|
b040d492cd | |||
ea85c1b46e | |||
6bec6f92c4 | |||
930a0ea73b | |||
7e2f56f41a | |||
e90f5f03f3 | |||
0e090ae112 | |||
99496706c5 | |||
f146553dea | |||
59e8f70e55 | |||
4c8365fe88 | |||
52af0674c1 | |||
938b90e5f2 | |||
55cc8584bd |
15
Cargo.lock
generated
15
Cargo.lock
generated
|
@ -186,12 +186,6 @@ dependencies = [
|
|||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "foldhash"
|
||||
version = "0.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f81ec6369c545a7d40e4589b5597581fa1c441fe1cce96dd1de43159910a36a2"
|
||||
|
||||
[[package]]
|
||||
name = "fsevent-sys"
|
||||
version = "4.1.0"
|
||||
|
@ -283,9 +277,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.159"
|
||||
version = "0.2.160"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "561d97a539a36e26a9a5fad1ea11a3039a67714694aaa379433e580854bc3dc5"
|
||||
checksum = "f0b21006cd1874ae9e650973c565615676dc4a274c965bb0a73796dac838ce4f"
|
||||
|
||||
[[package]]
|
||||
name = "libredox"
|
||||
|
@ -410,9 +404,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.87"
|
||||
version = "1.0.88"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b3e4daa0dcf6feba26f985457cdf104d4b4256fc5a09547140f3631bb076b19a"
|
||||
checksum = "7c3a7fc5db1e57d5a779a352c8cdb57b29aa4c40cc69c3a68a7fedc815fbf2f9"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
@ -455,7 +449,6 @@ dependencies = [
|
|||
"anyhow",
|
||||
"clap",
|
||||
"crossterm",
|
||||
"foldhash",
|
||||
"notify",
|
||||
"os_pipe",
|
||||
"rustix",
|
||||
|
|
|
@ -49,7 +49,6 @@ include = [
|
|||
anyhow = "1.0.89"
|
||||
clap = { version = "4.5.20", features = ["derive"] }
|
||||
crossterm = { version = "0.28.1", default-features = false, features = ["windows", "events"] }
|
||||
foldhash = "0.1.3"
|
||||
notify = { version = "6.1.1", default-features = false, features = ["macos_fsevent"] }
|
||||
os_pipe = "1.2.1"
|
||||
rustlings-macros = { path = "rustlings-macros", version = "=6.3.0" }
|
||||
|
|
12
README.md
12
README.md
|
@ -124,14 +124,13 @@ The list allows you to…
|
|||
|
||||
- See the status of all exercises (done or pending)
|
||||
- `c`: Continue at another exercise (temporarily skip some exercises or go back to a previous one)
|
||||
- `r`: Reset status and file of an exercise (you need to _reload/reopen_ its file in your editor afterwards)
|
||||
- `r`: Reset status and file of the selected exercise (you need to _reload/reopen_ its file in your editor afterwards)
|
||||
|
||||
See the footer of the list for all possible keys.
|
||||
|
||||
## Continuing On
|
||||
## Questions?
|
||||
|
||||
Once you've completed Rustlings, put your new knowledge to good use!
|
||||
Continue practicing your Rust skills by building your own projects, contributing to Rustlings, or finding other open-source projects to contribute to.
|
||||
If you need any help while doing the exercises and the builtin-hints aren't helpful, feel free to ask in the [_Q&A_ category of the discussions](https://github.com/rust-lang/rustlings/discussions/categories/q-a?discussions_q=) if your question wasn't asked yet 💡
|
||||
|
||||
## Third-Party Exercises
|
||||
|
||||
|
@ -144,6 +143,11 @@ Do you want to create your own set of Rustlings exercises to focus on some speci
|
|||
Or do you want to translate the original Rustlings exercises?
|
||||
Then follow the the guide about [third-party exercises](https://github.com/rust-lang/rustlings/blob/main/THIRD_PARTY_EXERCISES.md)!
|
||||
|
||||
## Continuing On
|
||||
|
||||
Once you've completed Rustlings, put your new knowledge to good use!
|
||||
Continue practicing your Rust skills by building your own projects, contributing to Rustlings, or finding other open-source projects to contribute to.
|
||||
|
||||
## Uninstalling Rustlings
|
||||
|
||||
If you want to remove Rustlings from your system, run the following command:
|
||||
|
|
|
@ -5,9 +5,6 @@ disallowed-types = [
|
|||
]
|
||||
|
||||
disallowed-methods = [
|
||||
# We use `foldhash` instead of the default hasher.
|
||||
"std::collections::HashSet::new",
|
||||
"std::collections::HashSet::with_capacity",
|
||||
# Inefficient. Use `.queue(…)` instead.
|
||||
"crossterm::style::style",
|
||||
# Use `thread::Builder::spawn` instead and handle the error.
|
||||
|
|
|
@ -116,6 +116,8 @@ bin = [
|
|||
{ name = "generics1_sol", path = "../solutions/14_generics/generics1.rs" },
|
||||
{ name = "generics2", path = "../exercises/14_generics/generics2.rs" },
|
||||
{ name = "generics2_sol", path = "../solutions/14_generics/generics2.rs" },
|
||||
{ name = "generics3", path = "../exercises/14_generics/generics3.rs" },
|
||||
{ name = "generics3_sol", path = "../solutions/14_generics/generics3.rs" },
|
||||
{ name = "traits1", path = "../exercises/15_traits/traits1.rs" },
|
||||
{ name = "traits1_sol", path = "../solutions/15_traits/traits1.rs" },
|
||||
{ name = "traits2", path = "../exercises/15_traits/traits2.rs" },
|
||||
|
|
|
@ -17,7 +17,7 @@ struct TeamScores {
|
|||
|
||||
fn build_scores_table(results: &str) -> HashMap<&str, TeamScores> {
|
||||
// The name of the team is the key and its associated struct is the value.
|
||||
let mut scores = HashMap::new();
|
||||
let mut scores = HashMap::<&str, TeamScores>::new();
|
||||
|
||||
for line in results.lines() {
|
||||
let mut split_iterator = line.split(',');
|
||||
|
|
54
exercises/14_generics/generics3.rs
Normal file
54
exercises/14_generics/generics3.rs
Normal file
|
@ -0,0 +1,54 @@
|
|||
// generics3.rs
|
||||
// Execute `rustlings hint generics3` or use the `hint` watch subcommand for a hint.
|
||||
|
||||
// This function should take an array of `Option` elements and returns array of not None elements
|
||||
// TODO fix this function signature
|
||||
fn into_dispose_nulls(list: Vec<Option<&str>>) -> Vec<&str> {
|
||||
list.into_iter().flatten().collect()
|
||||
}
|
||||
|
||||
fn main() {
|
||||
// You can optionally experiment here.
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn store_str_on_list() {
|
||||
let names_list = vec![Some("maria"), Some("jacob"), None, Some("kacper"), None];
|
||||
let only_values = into_dispose_nulls(names_list);
|
||||
assert_eq!(only_values.len(), 3);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn store_numbers_on_list() {
|
||||
let numbers_list = vec![Some(1), Some(2), None, Some(3)];
|
||||
let only_values = into_dispose_nulls(numbers_list);
|
||||
assert_eq!(only_values.len(), 3);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn store_custom_type_on_list() {
|
||||
#[allow(dead_code)]
|
||||
struct Rectangle {
|
||||
width: i32,
|
||||
height: i32,
|
||||
}
|
||||
impl Rectangle {
|
||||
fn new(width: i32, height: i32) -> Self {
|
||||
Self { width, height }
|
||||
}
|
||||
}
|
||||
|
||||
let custom_list = vec![
|
||||
Some(Rectangle::new(1, 2)),
|
||||
None,
|
||||
None,
|
||||
Some(Rectangle::new(3, 4)),
|
||||
];
|
||||
let only_values = into_dispose_nulls(custom_list);
|
||||
assert_eq!(only_values.len(), 2);
|
||||
}
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
// This program spawns multiple threads that each run for at least 250ms, and
|
||||
// each thread returns how much time they took to complete. The program should
|
||||
// This program spawns multiple threads that each runs for at least 250ms, and
|
||||
// each thread returns how much time it took to complete. The program should
|
||||
// wait until all the spawned threads have finished and should collect their
|
||||
// return values into a vector.
|
||||
|
||||
|
|
|
@ -575,12 +575,8 @@ https://doc.rust-lang.org/book/ch08-03-hash-maps.html#only-inserting-a-value-if-
|
|||
name = "hashmaps3"
|
||||
dir = "11_hashmaps"
|
||||
hint = """
|
||||
Hint 1: Use the `entry()` and `or_insert()` (or `or_insert_with()`) methods of
|
||||
`HashMap` to insert the default value of `TeamScores` if a team doesn't
|
||||
exist in the table yet.
|
||||
|
||||
Learn more in The Book:
|
||||
https://doc.rust-lang.org/book/ch08-03-hash-maps.html#only-inserting-a-value-if-the-key-has-no-value
|
||||
Hint 1: Use the `entry()` and `or_default()` methods of `HashMap` to insert the
|
||||
default value of `TeamScores` if a team doesn't exist in the table yet.
|
||||
|
||||
Hint 2: If there is already an entry for a given key, the value returned by
|
||||
`entry()` can be updated based on the existing value.
|
||||
|
@ -749,6 +745,17 @@ hint = """
|
|||
Related section in The Book:
|
||||
https://doc.rust-lang.org/book/ch10-01-syntax.html#in-method-definitions"""
|
||||
|
||||
[[exercises]]
|
||||
name = "generics3"
|
||||
dir = "14_generics"
|
||||
hint = """
|
||||
Vectors in Rust use generics to create dynamically-sized arrays of any type.
|
||||
The `into_dispose_nulls` function takes a vector as an argument, but only accepts vectors that store the &str type.
|
||||
To allow the function to accept vectors that store any type, you can leverage your knowledge about generics.
|
||||
If you're unsure how to proceed, please refer to the Rust Book at:
|
||||
https://doc.rust-lang.org/book/ch10-01-syntax.html#in-function-definitions.
|
||||
"""
|
||||
|
||||
# TRAITS
|
||||
|
||||
[[exercises]]
|
||||
|
|
|
@ -17,7 +17,7 @@ struct TeamScores {
|
|||
|
||||
fn build_scores_table(results: &str) -> HashMap<&str, TeamScores> {
|
||||
// The name of the team is the key and its associated struct is the value.
|
||||
let mut scores = HashMap::new();
|
||||
let mut scores = HashMap::<&str, TeamScores>::new();
|
||||
|
||||
for line in results.lines() {
|
||||
let mut split_iterator = line.split(',');
|
||||
|
@ -28,17 +28,13 @@ fn build_scores_table(results: &str) -> HashMap<&str, TeamScores> {
|
|||
let team_2_score: u8 = split_iterator.next().unwrap().parse().unwrap();
|
||||
|
||||
// Insert the default with zeros if a team doesn't exist yet.
|
||||
let team_1 = scores
|
||||
.entry(team_1_name)
|
||||
.or_insert_with(TeamScores::default);
|
||||
let team_1 = scores.entry(team_1_name).or_default();
|
||||
// Update the values.
|
||||
team_1.goals_scored += team_1_score;
|
||||
team_1.goals_conceded += team_2_score;
|
||||
|
||||
// Similarly for the second team.
|
||||
let team_2 = scores
|
||||
.entry(team_2_name)
|
||||
.or_insert_with(TeamScores::default);
|
||||
let team_2 = scores.entry(team_2_name).or_default();
|
||||
team_2.goals_scored += team_2_score;
|
||||
team_2.goals_conceded += team_1_score;
|
||||
}
|
||||
|
|
53
solutions/14_generics/generics3.rs
Normal file
53
solutions/14_generics/generics3.rs
Normal file
|
@ -0,0 +1,53 @@
|
|||
// generics3.rs
|
||||
// Execute `rustlings hint generics3` or use the `hint` watch subcommand for a hint.
|
||||
|
||||
// Here we added generic type `T` to function signature
|
||||
// Now this function can be used with vector of any
|
||||
fn into_dispose_nulls<T>(list: Vec<Option<T>>) -> Vec<T> {
|
||||
list.into_iter().flatten().collect()
|
||||
}
|
||||
|
||||
fn main() {
|
||||
// You can optionally experiment here.
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn store_str_on_list() {
|
||||
let names_list = vec![Some("maria"), Some("jacob"), None, Some("kacper"), None];
|
||||
let only_values = into_dispose_nulls(names_list);
|
||||
assert_eq!(only_values.len(), 3);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn store_numbers_on_list() {
|
||||
let numbers_list = vec![Some(1), Some(2), None, Some(3)];
|
||||
let only_values = into_dispose_nulls(numbers_list);
|
||||
assert_eq!(only_values.len(), 3);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn store_custom_type_on_list() {
|
||||
struct Rectangle {
|
||||
width: i32,
|
||||
height: i32,
|
||||
}
|
||||
impl Rectangle {
|
||||
fn new(width: i32, height: i32) -> Self {
|
||||
Self { width, height }
|
||||
}
|
||||
}
|
||||
|
||||
let custom_list = vec![
|
||||
Some(Rectangle::new(1, 2)),
|
||||
None,
|
||||
None,
|
||||
Some(Rectangle::new(3, 4)),
|
||||
];
|
||||
let only_values = into_dispose_nulls(custom_list);
|
||||
assert_eq!(only_values.len(), 2);
|
||||
}
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
// This program spawns multiple threads that each run for at least 250ms, and
|
||||
// each thread returns how much time they took to complete. The program should
|
||||
// This program spawns multiple threads that each runs for at least 250ms, and
|
||||
// each thread returns how much time it took to complete. The program should
|
||||
// wait until all the spawned threads have finished and should collect their
|
||||
// return values into a vector.
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
use anyhow::{bail, Context, Error, Result};
|
||||
use crossterm::{cursor, terminal, QueueableCommand};
|
||||
use std::{
|
||||
collections::HashSet,
|
||||
env,
|
||||
fs::{File, OpenOptions},
|
||||
io::{Read, Seek, StdoutLock, Write},
|
||||
|
@ -16,7 +17,6 @@ use std::{
|
|||
use crate::{
|
||||
clear_terminal,
|
||||
cmd::CmdRunner,
|
||||
collections::hash_set_with_capacity,
|
||||
embedded::EMBEDDED_FILES,
|
||||
exercise::{Exercise, RunnableExercise},
|
||||
info_file::ExerciseInfo,
|
||||
|
@ -146,7 +146,7 @@ impl AppState {
|
|||
break 'block StateFileStatus::NotRead;
|
||||
}
|
||||
|
||||
let mut done_exercises = hash_set_with_capacity(exercises.len());
|
||||
let mut done_exercises = HashSet::with_capacity(exercises.len());
|
||||
|
||||
for done_exercise_name in lines {
|
||||
if done_exercise_name.is_empty() {
|
||||
|
|
|
@ -125,7 +125,7 @@ pub struct CargoSubcommand<'out> {
|
|||
output: Option<&'out mut Vec<u8>>,
|
||||
}
|
||||
|
||||
impl<'out> CargoSubcommand<'out> {
|
||||
impl CargoSubcommand<'_> {
|
||||
#[inline]
|
||||
pub fn args<'arg, I>(&mut self, args: I) -> &mut Self
|
||||
where
|
||||
|
|
|
@ -1,9 +0,0 @@
|
|||
use foldhash::fast::FixedState;
|
||||
|
||||
/// DOS attacks aren't a concern for Rustlings. Therefore, we use `foldhash` with a fixed state.
|
||||
pub type HashSet<T> = std::collections::HashSet<T, FixedState>;
|
||||
|
||||
#[inline]
|
||||
pub fn hash_set_with_capacity<T>(capacity: usize) -> HashSet<T> {
|
||||
HashSet::with_capacity_and_hasher(capacity, FixedState::default())
|
||||
}
|
|
@ -1,6 +1,7 @@
|
|||
use anyhow::{anyhow, bail, Context, Error, Result};
|
||||
use std::{
|
||||
cmp::Ordering,
|
||||
collections::HashSet,
|
||||
fs::{self, read_dir, OpenOptions},
|
||||
io::{self, Read, Write},
|
||||
path::{Path, PathBuf},
|
||||
|
@ -11,7 +12,6 @@ use std::{
|
|||
use crate::{
|
||||
cargo_toml::{append_bins, bins_start_end_ind, BINS_BUFFER_CAPACITY},
|
||||
cmd::CmdRunner,
|
||||
collections::{hash_set_with_capacity, HashSet},
|
||||
exercise::{RunnableExercise, OUTPUT_CAPACITY},
|
||||
info_file::{ExerciseInfo, InfoFile},
|
||||
CURRENT_FORMAT_VERSION,
|
||||
|
@ -53,8 +53,8 @@ fn check_cargo_toml(
|
|||
|
||||
// Check the info of all exercises and return their paths in a set.
|
||||
fn check_info_file_exercises(info_file: &InfoFile) -> Result<HashSet<PathBuf>> {
|
||||
let mut names = hash_set_with_capacity(info_file.exercises.len());
|
||||
let mut paths = hash_set_with_capacity(info_file.exercises.len());
|
||||
let mut names = HashSet::with_capacity(info_file.exercises.len());
|
||||
let mut paths = HashSet::with_capacity(info_file.exercises.len());
|
||||
|
||||
let mut file_buf = String::with_capacity(1 << 14);
|
||||
for exercise_info in &info_file.exercises {
|
||||
|
@ -282,7 +282,7 @@ fn check_solutions(
|
|||
.collect::<Result<Vec<_>, _>>()
|
||||
.context("Failed to spawn a thread to check a solution")?;
|
||||
|
||||
let mut sol_paths = hash_set_with_capacity(info_file.exercises.len());
|
||||
let mut sol_paths = HashSet::with_capacity(info_file.exercises.len());
|
||||
let mut fmt_cmd = Command::new("rustfmt");
|
||||
fmt_cmd
|
||||
.arg("--check")
|
||||
|
|
|
@ -105,6 +105,28 @@ impl<'a> ListState<'a> {
|
|||
);
|
||||
}
|
||||
|
||||
fn draw_exericse_name(&self, writer: &mut MaxLenWriter, exercise: &Exercise) -> io::Result<()> {
|
||||
if !self.search_query.is_empty() {
|
||||
if let Some((pre_highlight, highlight, post_highlight)) = exercise
|
||||
.name
|
||||
.find(&self.search_query)
|
||||
.and_then(|ind| exercise.name.split_at_checked(ind))
|
||||
.and_then(|(pre_highlight, rest)| {
|
||||
rest.split_at_checked(self.search_query.len())
|
||||
.map(|x| (pre_highlight, x.0, x.1))
|
||||
})
|
||||
{
|
||||
writer.write_str(pre_highlight)?;
|
||||
writer.stdout.queue(SetForegroundColor(Color::Magenta))?;
|
||||
writer.write_str(highlight)?;
|
||||
writer.stdout.queue(ResetColor)?;
|
||||
return writer.write_str(post_highlight);
|
||||
}
|
||||
}
|
||||
|
||||
writer.write_str(exercise.name)
|
||||
}
|
||||
|
||||
fn draw_rows(
|
||||
&self,
|
||||
stdout: &mut StdoutLock,
|
||||
|
@ -147,10 +169,10 @@ impl<'a> ListState<'a> {
|
|||
writer.stdout.queue(SetForegroundColor(Color::Yellow))?;
|
||||
writer.write_ascii(b"PENDING ")?;
|
||||
}
|
||||
|
||||
writer.stdout.queue(SetForegroundColor(Color::Reset))?;
|
||||
|
||||
writer.write_str(exercise.name)?;
|
||||
self.draw_exericse_name(&mut writer, exercise)?;
|
||||
|
||||
writer.write_ascii(&self.name_col_padding[exercise.name.len()..])?;
|
||||
|
||||
// The list links aren't shown correctly in VS Code on Windows.
|
||||
|
|
|
@ -13,7 +13,6 @@ use self::{app_state::AppState, dev::DevCommands, info_file::InfoFile};
|
|||
mod app_state;
|
||||
mod cargo_toml;
|
||||
mod cmd;
|
||||
mod collections;
|
||||
mod dev;
|
||||
mod embedded;
|
||||
mod exercise;
|
||||
|
|
24
src/term.rs
24
src/term.rs
|
@ -11,15 +11,15 @@ use std::{
|
|||
|
||||
use crate::app_state::CheckProgress;
|
||||
|
||||
pub struct MaxLenWriter<'a, 'b> {
|
||||
pub stdout: &'a mut StdoutLock<'b>,
|
||||
pub struct MaxLenWriter<'a, 'lock> {
|
||||
pub stdout: &'a mut StdoutLock<'lock>,
|
||||
len: usize,
|
||||
max_len: usize,
|
||||
}
|
||||
|
||||
impl<'a, 'b> MaxLenWriter<'a, 'b> {
|
||||
impl<'a, 'lock> MaxLenWriter<'a, 'lock> {
|
||||
#[inline]
|
||||
pub fn new(stdout: &'a mut StdoutLock<'b>, max_len: usize) -> Self {
|
||||
pub fn new(stdout: &'a mut StdoutLock<'lock>, max_len: usize) -> Self {
|
||||
Self {
|
||||
stdout,
|
||||
len: 0,
|
||||
|
@ -34,13 +34,13 @@ impl<'a, 'b> MaxLenWriter<'a, 'b> {
|
|||
}
|
||||
}
|
||||
|
||||
pub trait CountedWrite<'a> {
|
||||
pub trait CountedWrite<'lock> {
|
||||
fn write_ascii(&mut self, ascii: &[u8]) -> io::Result<()>;
|
||||
fn write_str(&mut self, unicode: &str) -> io::Result<()>;
|
||||
fn stdout(&mut self) -> &mut StdoutLock<'a>;
|
||||
fn stdout(&mut self) -> &mut StdoutLock<'lock>;
|
||||
}
|
||||
|
||||
impl<'a, 'b> CountedWrite<'b> for MaxLenWriter<'a, 'b> {
|
||||
impl<'lock> CountedWrite<'lock> for MaxLenWriter<'_, 'lock> {
|
||||
fn write_ascii(&mut self, ascii: &[u8]) -> io::Result<()> {
|
||||
let n = ascii.len().min(self.max_len.saturating_sub(self.len));
|
||||
if n > 0 {
|
||||
|
@ -65,7 +65,7 @@ impl<'a, 'b> CountedWrite<'b> for MaxLenWriter<'a, 'b> {
|
|||
}
|
||||
|
||||
#[inline]
|
||||
fn stdout(&mut self) -> &mut StdoutLock<'b> {
|
||||
fn stdout(&mut self) -> &mut StdoutLock<'lock> {
|
||||
self.stdout
|
||||
}
|
||||
}
|
||||
|
@ -87,17 +87,17 @@ impl<'a> CountedWrite<'a> for StdoutLock<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
pub struct CheckProgressVisualizer<'a, 'b> {
|
||||
stdout: &'a mut StdoutLock<'b>,
|
||||
pub struct CheckProgressVisualizer<'a, 'lock> {
|
||||
stdout: &'a mut StdoutLock<'lock>,
|
||||
n_cols: usize,
|
||||
}
|
||||
|
||||
impl<'a, 'b> CheckProgressVisualizer<'a, 'b> {
|
||||
impl<'a, 'lock> CheckProgressVisualizer<'a, 'lock> {
|
||||
const CHECKING_COLOR: Color = Color::Blue;
|
||||
const DONE_COLOR: Color = Color::Green;
|
||||
const PENDING_COLOR: Color = Color::Red;
|
||||
|
||||
pub fn build(stdout: &'a mut StdoutLock<'b>, term_width: u16) -> io::Result<Self> {
|
||||
pub fn build(stdout: &'a mut StdoutLock<'lock>, term_width: u16) -> io::Result<Self> {
|
||||
clear_terminal(stdout)?;
|
||||
stdout.write_all("Checking all exercises…\n".as_bytes())?;
|
||||
|
||||
|
|
Loading…
Reference in a new issue