Compare commits

...

22 commits

Author SHA1 Message Date
Kacper Poneta 31b4a941cd
Merge 59e8f70e55 into 71f31d74bc 2024-08-18 20:05:56 +05:30
mo8it 71f31d74bc Update deps 2024-08-17 16:57:58 +02:00
mo8it 72e557b3a9 Break help footer on narrow terminals 2024-08-17 16:54:44 +02:00
mo8it 3eaccbb61a Restore the terminal after an error in the list 2024-08-17 16:49:07 +02:00
mo8it b678bd8ed2 Disable mouse in the list 2024-08-17 16:34:43 +02:00
mo8it 2baa140615 q only quits the list 2024-08-17 15:53:34 +02:00
mo8it e760f07767 Make it clear that reset only resets one exercise 2024-08-17 15:53:24 +02:00
mo8it ca5d5f0a49 Remove dot for copy-pasta 2024-08-17 15:45:02 +02:00
mo8it 69b4fd49fc Only take a u8 to avoid huge output 2024-08-17 14:59:00 +02:00
mo8it 36f315c344 Add "the" 2024-08-17 14:56:52 +02:00
mo8it 8016f5ca2d Remove unneeded comma 2024-08-17 14:55:58 +02:00
mo8it 8ef2ff1257 Remove "Hello and" 2024-08-17 14:54:13 +02:00
mo8it 6ce31defb6 Ignore stdout of git init 2024-08-17 14:40:09 +02:00
mo8it 0b3ad9141b Add exercise lints 2024-08-16 00:24:45 +02:00
mo8it c903db5c53 Add project lints 2024-08-16 00:24:45 +02:00
Mo 8a038b946c
Merge pull request #2084 from crd477/patch-1
fix typo
2024-08-16 00:12:58 +02:00
Chad Dougherty ed9740b72c
fix typo
Similarely -> Similarly in comment
2024-08-15 14:21:27 -04:00
mo8it 59e8f70e55 Format code 2024-07-12 18:31:23 +02:00
mo8it 4c8365fe88 Update dev/Cargo.toml 2024-07-12 18:25:01 +02:00
Kacper Poneta 52af0674c1 changed the task to make it more appropriate 2024-07-12 18:14:40 +02:00
Kacper Poneta 938b90e5f2 very small solution update 2024-07-11 22:55:48 +02:00
Kacper Poneta 55cc8584bd added exercise 2024-07-11 22:53:38 +02:00
16 changed files with 251 additions and 72 deletions

70
Cargo.lock generated
View file

@ -116,9 +116,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "clap"
version = "4.5.14"
version = "4.5.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c937d4061031a6d0c8da4b9a4f98a172fc2976dfb1c19213a9cf7d0d3c837e36"
checksum = "ed6719fffa43d0d87e5fd8caeab59be1554fb028cd30edc88fc4369b17971019"
dependencies = [
"clap_builder",
"clap_derive",
@ -126,9 +126,9 @@ dependencies = [
[[package]]
name = "clap_builder"
version = "4.5.14"
version = "4.5.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "85379ba512b21a328adf887e85f7742d12e96eb31f3ef077df4ffc26b506ffed"
checksum = "216aec2b177652e3846684cbfe25c9964d18ec45234f0f5da5157b207ed1aab6"
dependencies = [
"anstream",
"anstyle",
@ -197,7 +197,7 @@ checksum = "829d955a0bb380ef178a640b91779e3987da38c9aea133b20614cfed8cdea9c6"
dependencies = [
"bitflags 2.6.0",
"crossterm_winapi",
"mio 1.0.1",
"mio 1.0.2",
"parking_lot",
"rustix",
"signal-hook",
@ -244,14 +244,14 @@ checksum = "9fc0510504f03c51ada170672ac806f1f105a88aa97a5281117e1ddc3368e51a"
[[package]]
name = "filetime"
version = "0.2.23"
version = "0.2.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1ee447700ac8aa0b2f2bd7bc4462ad686ba06baa6727ac149a2d6277f0d240fd"
checksum = "bf401df4a4e3872c4fe8151134cf483738e74b67fc934d6532c882b3d24a4550"
dependencies = [
"cfg-if",
"libc",
"redox_syscall 0.4.1",
"windows-sys 0.52.0",
"libredox",
"windows-sys 0.59.0",
]
[[package]]
@ -287,9 +287,9 @@ checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024"
[[package]]
name = "indexmap"
version = "2.3.0"
version = "2.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "de3fc2e30ba82dd1b3911c8de1ffc143c74a914a14e99514d7637e3099df5ea0"
checksum = "93ead53efc7ea8ed3cfb0c79fc8023fbb782a5432b52830b6518941cebe6505c"
dependencies = [
"equivalent",
"hashbrown",
@ -368,9 +368,20 @@ dependencies = [
[[package]]
name = "libc"
version = "0.2.155"
version = "0.2.156"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c"
checksum = "a5f43f184355eefb8d17fc948dbecf6c13be3c141f20d834ae842193a448c72a"
[[package]]
name = "libredox"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d"
dependencies = [
"bitflags 2.6.0",
"libc",
"redox_syscall",
]
[[package]]
name = "linux-raw-sys"
@ -423,9 +434,9 @@ dependencies = [
[[package]]
name = "mio"
version = "1.0.1"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4569e456d394deccd22ce1c1913e6ea0e54519f577285001215d33557431afe4"
checksum = "80e04d1dcff3aae0704555fe5fee3bcfaf3d1fdf8a7e521d5b9d2b42acb52cec"
dependencies = [
"hermit-abi",
"libc",
@ -497,7 +508,7 @@ checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8"
dependencies = [
"cfg-if",
"libc",
"redox_syscall 0.5.3",
"redox_syscall",
"smallvec",
"windows-targets 0.52.6",
]
@ -547,15 +558,6 @@ dependencies = [
"unicode-width",
]
[[package]]
name = "redox_syscall"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa"
dependencies = [
"bitflags 1.3.2",
]
[[package]]
name = "redox_syscall"
version = "0.5.3"
@ -633,18 +635,18 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
[[package]]
name = "serde"
version = "1.0.205"
version = "1.0.208"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e33aedb1a7135da52b7c21791455563facbbcc43d0f0f66165b42c21b3dfb150"
checksum = "cff085d2cb684faa248efb494c39b68e522822ac0de72ccf08109abde717cfb2"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.205"
version = "1.0.208"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "692d6f5ac90220161d6774db30c662202721e64aed9058d2c394f451261420c1"
checksum = "24008e81ff7613ed8e5ba0cfaf24e2c2f1e5b8a0495711e44fcd4882fca62bcf"
dependencies = [
"proc-macro2",
"quote",
@ -653,9 +655,9 @@ dependencies = [
[[package]]
name = "serde_json"
version = "1.0.122"
version = "1.0.125"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "784b6203951c57ff748476b126ccb5e8e2959a5c19e5c617ab1956be3dbc68da"
checksum = "83c8e735a073ccf5be70aa8066aa984eaf2fa000db6c8d0100ae605b366d31ed"
dependencies = [
"itoa",
"memchr",
@ -689,7 +691,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34db1a06d485c9142248b7a054f034b349b212551f3dfd19c94d45a754a217cd"
dependencies = [
"libc",
"mio 1.0.1",
"mio 1.0.2",
"signal-hook",
]
@ -744,9 +746,9 @@ dependencies = [
[[package]]
name = "syn"
version = "2.0.72"
version = "2.0.74"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc4b9b9bf2add8093d3f2c0204471e951b2285580335de42f9d2534f3ae7a8af"
checksum = "1fceb41e3d546d0bd83421d3409b1460cc7444cd389341a4c880fe7a042cb3d7"
dependencies = [
"proc-macro2",
"quote",

View file

@ -19,7 +19,7 @@ edition = "2021" # On Update: Update the edition of the `rustfmt` command that c
rust-version = "1.80"
[workspace.dependencies]
serde = { version = "1.0.205", features = ["derive"] }
serde = { version = "1.0.208", features = ["derive"] }
toml_edit = { version = "0.22.20", default-features = false, features = ["parse", "serde"] }
[package]
@ -48,12 +48,12 @@ include = [
[dependencies]
ahash = { version = "0.8.11", default-features = false }
anyhow = "1.0.86"
clap = { version = "4.5.14", features = ["derive"] }
clap = { version = "4.5.16", features = ["derive"] }
notify-debouncer-mini = { version = "0.4.1", default-features = false }
os_pipe = "1.2.1"
ratatui = { version = "0.28.0", default-features = false, features = ["crossterm"] }
rustlings-macros = { path = "rustlings-macros", version = "=6.2.0" }
serde_json = "1.0.122"
serde_json = "1.0.125"
serde.workspace = true
toml_edit.workspace = true
@ -69,6 +69,18 @@ panic = "abort"
[package.metadata.release]
pre-release-hook = ["./release-hook.sh"]
[workspace.lints.rust]
unsafe_code = "forbid"
unstable_features = "forbid"
[workspace.lints.clippy]
empty_loop = "forbid"
infinite_loop = "deny"
mem_forget = "deny"
dbg_macro = "warn"
todo = "warn"
# TODO: Remove after the following fix is released: https://github.com/rust-lang/rust-clippy/pull/13102
[lints.clippy]
needless_option_as_deref = "allow"
[lints]
workspace = true

View file

@ -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" },
@ -201,3 +203,19 @@ panic = "abort"
[profile.dev]
panic = "abort"
[lints.rust]
# You shouldn't write unsafe code in Rustlings
unsafe_code = "forbid"
# You don't need unstable features in Rustlings and shouldn't rely on them while learning Rust
unstable_features = "forbid"
[lints.clippy]
# You forgot a `todo!()`
todo = "forbid"
# This can only happen by mistake in Rustlings
empty_loop = "forbid"
# No infinite loops are needed in Rustlings
infinite_loop = "deny"
# You shouldn't leak memory while still learning Rust
mem_forget = "deny"

View file

@ -1,4 +1,4 @@
// TODO: We sometimes encourage you to keep trying things on a given exercise,
// TODO: We sometimes encourage you to keep trying things on a given exercise
// even after you already figured it out. If you got everything working and feel
// ready for the next exercise, enter `n` in the terminal.
//
@ -6,8 +6,7 @@
// Try adding a new `println!` and check the updated output in the terminal.
fn main() {
println!("Hello and");
println!(r#" welcome to... "#);
println!(r#" Welcome to... "#);
println!(r#" _ _ _ "#);
println!(r#" _ __ _ _ ___| |_| (_)_ __ __ _ ___ "#);
println!(r#" | '__| | | / __| __| | | '_ \ / _` / __| "#);

View file

@ -1,5 +1,5 @@
fn main() {
// TODO: Add missing keyword.
// TODO: Add the missing keyword.
x = 5;
println!("x has the value {x}");

View file

@ -1,4 +1,4 @@
fn call_me(num: u32) {
fn call_me(num: u8) {
for i in 0..num {
println!("Ring! Call number {}", i + 1);
}

View file

@ -6,7 +6,7 @@
// of `Option<String>`.
fn generate_nametag_text(name: String) -> Option<String> {
if name.is_empty() {
// Empty names aren't allowed.
// Empty names aren't allowed
None
} else {
Some(format!("Hi! My name is {name}"))

View 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);
}
}

View file

@ -19,3 +19,6 @@ proc-macro = true
quote = "1.0.36"
serde.workspace = true
toml_edit.workspace = true
[lints]
workspace = true

View file

@ -744,6 +744,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]]

View file

@ -1,4 +1,4 @@
fn call_me(num: u32) {
fn call_me(num: u8) {
for i in 0..num {
println!("Ring! Call number {}", i + 1);
}

View file

@ -35,7 +35,7 @@ fn build_scores_table(results: &str) -> HashMap<&str, TeamScores> {
team_1.goals_scored += team_1_score;
team_1.goals_conceded += team_2_score;
// Similarely for the second team.
// Similarly for the second team.
let team_2 = scores
.entry(team_2_name)
.or_insert_with(TeamScores::default);

View 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);
}
}

View file

@ -139,13 +139,14 @@ pub fn init() -> Result<()> {
let _ = Command::new("git")
.arg("init")
.stdin(Stdio::null())
.stdout(Stdio::null())
.stderr(Stdio::null())
.status();
}
writeln!(
stdout,
"\n{}\n\n{}",
"{}\n\n{}",
"Initialization done ✓".green(),
POST_INIT_MSG.bold(),
)?;

View file

@ -1,14 +1,14 @@
use anyhow::Result;
use anyhow::{Context, Result};
use ratatui::{
backend::CrosstermBackend,
crossterm::{
event::{self, Event, KeyCode, KeyEventKind},
event::{self, DisableMouseCapture, EnableMouseCapture, Event, KeyCode, KeyEventKind},
terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen},
ExecutableCommand,
QueueableCommand,
},
Terminal,
};
use std::io;
use std::io::{self, StdoutLock, Write};
use crate::app_state::AppState;
@ -16,12 +16,8 @@ use self::state::{Filter, UiState};
mod state;
pub fn list(app_state: &mut AppState) -> Result<()> {
let mut stdout = io::stdout().lock();
stdout.execute(EnterAlternateScreen)?;
enable_raw_mode()?;
let mut terminal = Terminal::new(CrosstermBackend::new(&mut stdout))?;
fn handle_list(app_state: &mut AppState, stdout: &mut StdoutLock) -> Result<()> {
let mut terminal = Terminal::new(CrosstermBackend::new(stdout))?;
terminal.clear()?;
let mut ui_state = UiState::new(app_state);
@ -30,7 +26,7 @@ pub fn list(app_state: &mut AppState) -> Result<()> {
terminal.try_draw(|frame| ui_state.draw(frame).map_err(io::Error::other))?;
let key = loop {
match event::read()? {
match event::read().context("Failed to read terminal event")? {
Event::Key(key) => match key.kind {
KeyEventKind::Press | KeyEventKind::Repeat => break key,
KeyEventKind::Release => (),
@ -85,9 +81,25 @@ pub fn list(app_state: &mut AppState) -> Result<()> {
}
}
drop(terminal);
stdout.execute(LeaveAlternateScreen)?;
disable_raw_mode()?;
Ok(())
}
pub fn list(app_state: &mut AppState) -> Result<()> {
let mut stdout = io::stdout().lock();
stdout
.queue(EnterAlternateScreen)?
.queue(EnableMouseCapture)?
.flush()?;
enable_raw_mode()?;
let res = handle_list(app_state, &mut stdout);
// Restore the terminal even if we got an error.
stdout
.queue(LeaveAlternateScreen)?
.queue(DisableMouseCapture)?
.flush()?;
disable_raw_mode()?;
res
}

View file

@ -2,11 +2,11 @@ use anyhow::{Context, Result};
use ratatui::{
layout::{Constraint, Rect},
style::{Style, Stylize},
text::{Line, Span},
text::{Span, Text},
widgets::{Block, Borders, HighlightSpacing, Paragraph, Row, Table, TableState},
Frame,
};
use std::fmt::Write;
use std::{fmt::Write, mem};
use crate::{app_state::AppState, progress_bar::progress_bar_ratatui};
@ -162,6 +162,9 @@ impl<'a> UiState<'a> {
pub fn draw(&mut self, frame: &mut Frame) -> Result<()> {
let area = frame.area();
let narrow = area.width < 95;
let narrow_u16 = u16::from(narrow);
let table_height = area.height - 3 - narrow_u16;
frame.render_stateful_widget(
&self.table,
@ -169,7 +172,7 @@ impl<'a> UiState<'a> {
x: 0,
y: 0,
width: area.width,
height: area.height - 3,
height: table_height,
},
&mut self.table_state,
);
@ -183,7 +186,7 @@ impl<'a> UiState<'a> {
.block(Block::default().borders(Borders::BOTTOM)),
Rect {
x: 0,
y: area.height - 3,
y: table_height,
width: area.width,
height: 2,
},
@ -191,10 +194,19 @@ impl<'a> UiState<'a> {
let message = if self.message.is_empty() {
// Help footer.
let mut text = Text::default();
let mut spans = Vec::with_capacity(4);
spans.push(Span::raw(
"↓/j ↑/k home/g end/G │ <c>ontinue at │ <r>eset │ filter ",
"↓/j ↑/k home/g end/G │ <c>ontinue at │ <r>eset exercise │",
));
if narrow {
text.push_line(mem::take(&mut spans));
spans.push(Span::raw("filter "));
} else {
spans.push(Span::raw(" filter "));
}
match self.filter {
Filter::Done => {
spans.push("<d>one".underlined().magenta());
@ -206,18 +218,20 @@ impl<'a> UiState<'a> {
}
Filter::None => spans.push(Span::raw("<d>one/<p>ending")),
}
spans.push(Span::raw(" │ <q>uit"));
Line::from(spans)
spans.push(Span::raw(" │ <q>uit list"));
text.push_line(spans);
text
} else {
Line::from(self.message.as_str().light_blue())
Text::from(self.message.as_str().light_blue())
};
frame.render_widget(
message,
Rect {
x: 0,
y: area.height - 1,
y: table_height + 2,
width: area.width,
height: 1,
height: 1 + narrow_u16,
},
);