From e5efc68a9101d7d7e38263c8a6ee44dda991fc6a Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 28 Mar 2024 17:34:48 +0100 Subject: Done macro --- Cargo.lock | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'Cargo.lock') diff --git a/Cargo.lock b/Cargo.lock index f4853d0..e432072 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -574,6 +574,7 @@ dependencies = [ "indicatif", "notify-debouncer-mini", "predicates", + "rustlings-macros", "serde", "serde_json", "shlex", @@ -582,6 +583,13 @@ dependencies = [ "winnow", ] +[[package]] +name = "rustlings-macros" +version = "5.6.1" +dependencies = [ + "quote", +] + [[package]] name = "ryu" version = "1.0.17" -- cgit v1.2.3 From 3959570221c88bf7bebbc7427236ae5c90d9d630 Mon Sep 17 00:00:00 2001 From: mo8it Date: Fri, 29 Mar 2024 01:25:21 +0100 Subject: Bump version to v6 --- Cargo.lock | 4 ++-- Cargo.toml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'Cargo.lock') diff --git a/Cargo.lock b/Cargo.lock index e432072..d8e5b72 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -564,7 +564,7 @@ dependencies = [ [[package]] name = "rustlings" -version = "5.6.1" +version = "6.0.0" dependencies = [ "anyhow", "assert_cmd", @@ -585,7 +585,7 @@ dependencies = [ [[package]] name = "rustlings-macros" -version = "5.6.1" +version = "6.0.0" dependencies = [ "quote", ] diff --git a/Cargo.toml b/Cargo.toml index e08be8b..690aecc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,7 +2,7 @@ resolver = "2" [workspace.package] -version = "5.6.1" +version = "6.0.0" authors = [ "Liv ", "Carol (Nichols || Goulding) ", -- cgit v1.2.3 From 2b6f9fb6a7a33f074aa609b2da1ac084bc3ecd6b Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 4 Apr 2024 20:21:55 +0200 Subject: Add Ratatui --- Cargo.lock | 319 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-- Cargo.toml | 2 + 2 files changed, 312 insertions(+), 9 deletions(-) (limited to 'Cargo.lock') diff --git a/Cargo.lock b/Cargo.lock index d8e5b72..38f8170 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,18 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "ahash" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" +dependencies = [ + "cfg-if", + "once_cell", + "version_check", + "zerocopy", +] + [[package]] name = "aho-corasick" version = "1.1.3" @@ -11,6 +23,12 @@ dependencies = [ "memchr", ] +[[package]] +name = "allocator-api2" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0942ffc6dcaadf03badf6e6a2d0228460359d5e34b57ccdc720b7382dfbd5ec5" + [[package]] name = "anstream" version = "0.6.13" @@ -109,6 +127,21 @@ dependencies = [ "serde", ] +[[package]] +name = "cassowary" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df8670b8c7b9dae1793364eafadf7239c40d669904660c5960d74cfd80b46a53" + +[[package]] +name = "castaway" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a17ed5635fc8536268e5d4de1e22e81ac34419e5f052d4d51f4e01dcc263fcc" +dependencies = [ + "rustversion", +] + [[package]] name = "cfg-if" version = "1.0.0" @@ -143,10 +176,10 @@ version = "4.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "528131438037fd55894f62d6e9f068b8f45ac57ffa77517819645d10aed04f64" dependencies = [ - "heck", + "heck 0.5.0", "proc-macro2", "quote", - "syn", + "syn 2.0.58", ] [[package]] @@ -161,6 +194,19 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" +[[package]] +name = "compact_str" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f86b9c4c00838774a6d902ef931eff7470720c51d90c2e32cfe15dc304737b3f" +dependencies = [ + "castaway", + "cfg-if", + "itoa", + "ryu", + "static_assertions", +] + [[package]] name = "console" version = "0.15.8" @@ -189,6 +235,31 @@ version = "0.8.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345" +[[package]] +name = "crossterm" +version = "0.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f476fe445d41c9e991fd07515a6f463074b782242ccf4a5b7b1d1012e70824df" +dependencies = [ + "bitflags 2.5.0", + "crossterm_winapi", + "libc", + "mio", + "parking_lot", + "signal-hook", + "signal-hook-mio", + "winapi", +] + +[[package]] +name = "crossterm_winapi" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acdd7c62a3665c7f6830a51635d9ac9b23ed385797f70a83bb8bafe9c572ab2b" +dependencies = [ + "winapi", +] + [[package]] name = "difflib" version = "0.4.0" @@ -270,6 +341,16 @@ name = "hashbrown" version = "0.14.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" +dependencies = [ + "ahash", + "allocator-api2", +] + +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" [[package]] name = "heck" @@ -309,6 +390,12 @@ dependencies = [ "unicode-width", ] +[[package]] +name = "indoc" +version = "2.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b248f5224d1d606005e02c97f5aa4e88eeb230488bcc03bc9ca4d7991399f2b5" + [[package]] name = "inotify" version = "0.9.6" @@ -338,6 +425,15 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "itertools" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" +dependencies = [ + "either", +] + [[package]] name = "itoa" version = "1.0.11" @@ -382,17 +478,36 @@ version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c" +[[package]] +name = "lock_api" +version = "0.4.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45" +dependencies = [ + "autocfg", + "scopeguard", +] + [[package]] name = "log" version = "0.4.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" +[[package]] +name = "lru" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3262e75e648fce39813cb56ac41f3c3e3f65217ebf3844d818d1f9398cfb0dc" +dependencies = [ + "hashbrown", +] + [[package]] name = "memchr" -version = "2.7.1" +version = "2.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" +checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d" [[package]] name = "mio" @@ -457,6 +572,41 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3" +[[package]] +name = "once_cell" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" + +[[package]] +name = "parking_lot" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-targets 0.48.5", +] + +[[package]] +name = "paste" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" + [[package]] name = "portable-atomic" version = "1.6.0" @@ -511,6 +661,26 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "ratatui" +version = "0.26.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bcb12f8fbf6c62614b0d56eb352af54f6a22410c3b079eb53ee93c7b97dd31d8" +dependencies = [ + "bitflags 2.5.0", + "cassowary", + "compact_str", + "crossterm", + "indoc", + "itertools", + "lru", + "paste", + "stability", + "strum", + "unicode-segmentation", + "unicode-width", +] + [[package]] name = "redox_syscall" version = "0.4.1" @@ -570,10 +740,12 @@ dependencies = [ "assert_cmd", "clap", "console", + "crossterm", "glob", "indicatif", "notify-debouncer-mini", "predicates", + "ratatui", "rustlings-macros", "serde", "serde_json", @@ -590,6 +762,12 @@ dependencies = [ "quote", ] +[[package]] +name = "rustversion" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4" + [[package]] name = "ryu" version = "1.0.17" @@ -605,6 +783,12 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + [[package]] name = "serde" version = "1.0.197" @@ -622,7 +806,7 @@ checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.58", ] [[package]] @@ -651,17 +835,102 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" +[[package]] +name = "signal-hook" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8621587d4798caf8eb44879d42e56b9a93ea5dcd315a6487c357130095b62801" +dependencies = [ + "libc", + "signal-hook-registry", +] + +[[package]] +name = "signal-hook-mio" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29ad2e15f37ec9a6cc544097b78a1ec90001e9f71b81338ca39f430adaca99af" +dependencies = [ + "libc", + "mio", + "signal-hook", +] + +[[package]] +name = "signal-hook-registry" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" +dependencies = [ + "libc", +] + +[[package]] +name = "smallvec" +version = "1.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" + +[[package]] +name = "stability" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebd1b177894da2a2d9120208c3386066af06a488255caabc5de8ddca22dbc3ce" +dependencies = [ + "quote", + "syn 1.0.109", +] + +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + [[package]] name = "strsim" -version = "0.11.0" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] +name = "strum" +version = "0.26.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d8cec3501a5194c432b2b7976db6b7d10ec95c253208b45f83f7136aa985e29" +dependencies = [ + "strum_macros", +] + +[[package]] +name = "strum_macros" +version = "0.26.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ee073c9e4cd00e28217186dbe12796d692868f432bf2e97ee73bed0c56dfa01" +checksum = "c6cf59daf282c0a494ba14fd21610a0325f9f90ec9d1231dea26bcb1d696c946" +dependencies = [ + "heck 0.4.1", + "proc-macro2", + "quote", + "rustversion", + "syn 2.0.58", +] [[package]] name = "syn" -version = "2.0.55" +version = "1.0.109" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "002a1b3dbf967edfafc32655d0f377ab0bb7b994aa1d32c8cc7e9b8bf3ebb8f0" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.58" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44cfb93f38070beee36b3fef7d4f5a16f27751d94b187b666a5cc5e9b0d30687" dependencies = [ "proc-macro2", "quote", @@ -702,6 +971,12 @@ version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" +[[package]] +name = "unicode-segmentation" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202" + [[package]] name = "unicode-width" version = "0.1.11" @@ -714,6 +989,12 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + [[package]] name = "wait-timeout" version = "0.2.0" @@ -928,3 +1209,23 @@ name = "winsafe" version = "0.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d135d17ab770252ad95e9a872d365cf3090e3be864a34ab46f48555993efc904" + +[[package]] +name = "zerocopy" +version = "0.7.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74d4d3961e53fa4c9a25a8637fc2bfaf2595b3d3ae34875568a5cf64787716be" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.58", +] diff --git a/Cargo.toml b/Cargo.toml index 86187b4..2a22fce 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -29,8 +29,10 @@ edition.workspace = true anyhow = "1.0.81" clap = { version = "4.5.4", features = ["derive"] } console = "0.15.8" +crossterm = "0.27.0" indicatif = "0.17.8" notify-debouncer-mini = "0.4.1" +ratatui = "0.26.1" rustlings-macros = { path = "rustlings-macros" } serde_json = "1.0.115" serde = { version = "1.0.197", features = ["derive"] } -- cgit v1.2.3 From 9ea744a7104f441ef505db0a96e852f93d8c0bf4 Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 4 Apr 2024 20:27:30 +0200 Subject: Remove deps not needed in the TUI --- Cargo.lock | 42 ------------------------------------------ Cargo.toml | 2 -- 2 files changed, 44 deletions(-) (limited to 'Cargo.lock') diff --git a/Cargo.lock b/Cargo.lock index 38f8170..4aaec38 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -377,19 +377,6 @@ dependencies = [ "hashbrown", ] -[[package]] -name = "indicatif" -version = "0.17.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "763a5a8f45087d6bcea4222e7b72c291a054edf80e4ef6efd2a4979878c7bea3" -dependencies = [ - "console", - "instant", - "number_prefix", - "portable-atomic", - "unicode-width", -] - [[package]] name = "indoc" version = "2.0.5" @@ -416,15 +403,6 @@ dependencies = [ "libc", ] -[[package]] -name = "instant" -version = "0.1.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" -dependencies = [ - "cfg-if", -] - [[package]] name = "itertools" version = "0.12.1" @@ -566,12 +544,6 @@ dependencies = [ "autocfg", ] -[[package]] -name = "number_prefix" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3" - [[package]] name = "once_cell" version = "1.19.0" @@ -607,12 +579,6 @@ version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" -[[package]] -name = "portable-atomic" -version = "1.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7170ef9988bc169ba16dd36a7fa041e5c4cbeb6a35b76d4c03daded371eae7c0" - [[package]] name = "predicates" version = "3.1.0" @@ -742,14 +708,12 @@ dependencies = [ "console", "crossterm", "glob", - "indicatif", "notify-debouncer-mini", "predicates", "ratatui", "rustlings-macros", "serde", "serde_json", - "shlex", "toml_edit", "which", "winnow", @@ -829,12 +793,6 @@ dependencies = [ "serde", ] -[[package]] -name = "shlex" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" - [[package]] name = "signal-hook" version = "0.3.17" diff --git a/Cargo.toml b/Cargo.toml index 2a22fce..3c18741 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -30,13 +30,11 @@ anyhow = "1.0.81" clap = { version = "4.5.4", features = ["derive"] } console = "0.15.8" crossterm = "0.27.0" -indicatif = "0.17.8" notify-debouncer-mini = "0.4.1" ratatui = "0.26.1" rustlings-macros = { path = "rustlings-macros" } serde_json = "1.0.115" serde = { version = "1.0.197", features = ["derive"] } -shlex = "1.3.0" toml_edit = { version = "0.22.9", default-features = false, features = ["parse", "serde"] } which = "6.0.1" winnow = "0.6.5" -- cgit v1.2.3 From 445441ce25ec8658bcdec6b2038d17e893a5903f Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 4 Apr 2024 23:16:57 +0200 Subject: Make gen-dev-cargo-toml a separate package so that `cargo install` only installs `rustlings` --- Cargo.lock | 9 ++++++ Cargo.toml | 14 +++++++-- dev/Cargo.toml | 4 +-- gen-dev-cargo-toml/Cargo.toml | 10 +++++++ gen-dev-cargo-toml/src/main.rs | 64 ++++++++++++++++++++++++++++++++++++++++++ src/bin/gen-dev-cargo-toml.rs | 64 ------------------------------------------ tests/dev_cargo_bins.rs | 2 +- 7 files changed, 97 insertions(+), 70 deletions(-) create mode 100644 gen-dev-cargo-toml/Cargo.toml create mode 100644 gen-dev-cargo-toml/src/main.rs delete mode 100644 src/bin/gen-dev-cargo-toml.rs (limited to 'Cargo.lock') diff --git a/Cargo.lock b/Cargo.lock index 4aaec38..e03980c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -330,6 +330,15 @@ dependencies = [ "libc", ] +[[package]] +name = "gen-dev-cargo-toml" +version = "0.0.0" +dependencies = [ + "anyhow", + "serde", + "toml_edit", +] + [[package]] name = "glob" version = "0.3.1" diff --git a/Cargo.toml b/Cargo.toml index 3c18741..d80550a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,6 +6,9 @@ exclude = [ "tests/fixture/success", "dev", ] +members = [ + "gen-dev-cargo-toml", +] [workspace.package] version = "6.0.0" @@ -16,6 +19,11 @@ authors = [ license = "MIT" edition = "2021" +[workspace.dependencies] +anyhow = "1.0.81" +serde = { version = "1.0.197", features = ["derive"] } +toml_edit = { version = "0.22.9", default-features = false, features = ["parse", "serde"] } + [package] name = "rustlings" description = "Small exercises to get you used to reading and writing Rust code!" @@ -26,7 +34,7 @@ license.workspace = true edition.workspace = true [dependencies] -anyhow = "1.0.81" +anyhow.workspace = true clap = { version = "4.5.4", features = ["derive"] } console = "0.15.8" crossterm = "0.27.0" @@ -34,8 +42,8 @@ notify-debouncer-mini = "0.4.1" ratatui = "0.26.1" rustlings-macros = { path = "rustlings-macros" } serde_json = "1.0.115" -serde = { version = "1.0.197", features = ["derive"] } -toml_edit = { version = "0.22.9", default-features = false, features = ["parse", "serde"] } +serde.workspace = true +toml_edit.workspace = true which = "6.0.1" winnow = "0.6.5" diff --git a/dev/Cargo.toml b/dev/Cargo.toml index 7868b97..ed9b3ed 100644 --- a/dev/Cargo.toml +++ b/dev/Cargo.toml @@ -1,5 +1,5 @@ -# This file is a hack to allow using `cargo r` to test `rustlings` during development. -# You shouldn't edit it manually. It is created and updated by running `cargo run --bin gen-dev-cargo-toml`. +# This file is a hack to allow using `cargo run` to test `rustlings` during development. +# You shouldn't edit it manually. It is created and updated by running `cargo run -p gen-dev-cargo-toml`. bin = [ { name = "intro1", path = "../exercises/00_intro/intro1.rs" }, diff --git a/gen-dev-cargo-toml/Cargo.toml b/gen-dev-cargo-toml/Cargo.toml new file mode 100644 index 0000000..8922ae8 --- /dev/null +++ b/gen-dev-cargo-toml/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "gen-dev-cargo-toml" +publish = false +license.workspace = true +edition.workspace = true + +[dependencies] +anyhow.workspace = true +serde.workspace = true +toml_edit.workspace = true diff --git a/gen-dev-cargo-toml/src/main.rs b/gen-dev-cargo-toml/src/main.rs new file mode 100644 index 0000000..622762a --- /dev/null +++ b/gen-dev-cargo-toml/src/main.rs @@ -0,0 +1,64 @@ +// Generates `dev/Cargo.toml` such that it is synced with `info.toml`. +// `dev/Cargo.toml` is a hack to allow using `cargo run` to test `rustlings` +// during development. + +use anyhow::{bail, Context, Result}; +use serde::Deserialize; +use std::{ + fs::{self, create_dir}, + io::ErrorKind, +}; + +#[derive(Deserialize)] +struct Exercise { + name: String, + path: String, +} + +#[derive(Deserialize)] +struct InfoToml { + exercises: Vec, +} + +fn main() -> Result<()> { + let exercises = toml_edit::de::from_str::( + &fs::read_to_string("info.toml").context("Failed to read `info.toml`")?, + ) + .context("Failed to deserialize `info.toml`")? + .exercises; + + let mut buf = Vec::with_capacity(1 << 14); + + buf.extend_from_slice( + b"# This file is a hack to allow using `cargo run` to test `rustlings` during development. +# You shouldn't edit it manually. It is created and updated by running `cargo run -p gen-dev-cargo-toml`. + +bin = [\n", + ); + + for exercise in exercises { + buf.extend_from_slice(b" { name = \""); + buf.extend_from_slice(exercise.name.as_bytes()); + buf.extend_from_slice(b"\", path = \"../"); + buf.extend_from_slice(exercise.path.as_bytes()); + buf.extend_from_slice(b"\" },\n"); + } + + buf.extend_from_slice( + br#"] + +[package] +name = "rustlings" +edition = "2021" +publish = false +"#, + ); + + if let Err(e) = create_dir("dev") { + if e.kind() != ErrorKind::AlreadyExists { + bail!("Failed to create the `dev` directory: {e}"); + } + } + + fs::write("dev/Cargo.toml", buf).context("Failed to write `dev/Cargo.toml`") +} diff --git a/src/bin/gen-dev-cargo-toml.rs b/src/bin/gen-dev-cargo-toml.rs deleted file mode 100644 index ff8f31d..0000000 --- a/src/bin/gen-dev-cargo-toml.rs +++ /dev/null @@ -1,64 +0,0 @@ -// Generates `dev/Cargo.toml` such that it is synced with `info.toml`. -// `dev/Cargo.toml` is a hack to allow using `cargo r` to test `rustlings` -// during development. - -use anyhow::{bail, Context, Result}; -use serde::Deserialize; -use std::{ - fs::{self, create_dir}, - io::ErrorKind, -}; - -#[derive(Deserialize)] -struct Exercise { - name: String, - path: String, -} - -#[derive(Deserialize)] -struct InfoToml { - exercises: Vec, -} - -fn main() -> Result<()> { - let exercises = toml_edit::de::from_str::( - &fs::read_to_string("info.toml").context("Failed to read `info.toml`")?, - ) - .context("Failed to deserialize `info.toml`")? - .exercises; - - let mut buf = Vec::with_capacity(1 << 14); - - buf.extend_from_slice( - b"# This file is a hack to allow using `cargo r` to test `rustlings` during development. -# You shouldn't edit it manually. It is created and updated by running `cargo run --bin gen-dev-cargo-toml`. - -bin = [\n", - ); - - for exercise in exercises { - buf.extend_from_slice(b" { name = \""); - buf.extend_from_slice(exercise.name.as_bytes()); - buf.extend_from_slice(b"\", path = \"../"); - buf.extend_from_slice(exercise.path.as_bytes()); - buf.extend_from_slice(b"\" },\n"); - } - - buf.extend_from_slice( - br#"] - -[package] -name = "rustlings" -edition = "2021" -publish = false -"#, - ); - - if let Err(e) = create_dir("dev") { - if e.kind() != ErrorKind::AlreadyExists { - bail!("Failed to create the `dev` directory: {e}"); - } - } - - fs::write("dev/Cargo.toml", buf).context("Failed to write `dev/Cargo.toml`") -} diff --git a/tests/dev_cargo_bins.rs b/tests/dev_cargo_bins.rs index 7f1771b..ad4832f 100644 --- a/tests/dev_cargo_bins.rs +++ b/tests/dev_cargo_bins.rs @@ -1,5 +1,5 @@ // Makes sure that `dev/Cargo.toml` is synced with `info.toml`. -// When this test fails, you just need to run `cargo run --bin gen-dev-cargo-toml`. +// When this test fails, you just need to run `cargo run -p gen-dev-cargo-toml`. use serde::Deserialize; use std::fs; -- cgit v1.2.3 From b0f19fd862d659d2d4b01f2faa6b006fe2c60561 Mon Sep 17 00:00:00 2001 From: mo8it Date: Fri, 5 Apr 2024 03:04:53 +0200 Subject: Start with the TUI --- Cargo.lock | 26 ------- Cargo.toml | 1 - src/consts.rs | 59 ++++++++++++++ src/main.rs | 245 +++++----------------------------------------------------- src/tui.rs | 92 ++++++++++++++++++++++ src/verify.rs | 16 ++-- 6 files changed, 180 insertions(+), 259 deletions(-) create mode 100644 src/consts.rs create mode 100644 src/tui.rs (limited to 'Cargo.lock') diff --git a/Cargo.lock b/Cargo.lock index e03980c..33d3030 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -207,19 +207,6 @@ dependencies = [ "static_assertions", ] -[[package]] -name = "console" -version = "0.15.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e1f83fc076bd6dd27517eacdf25fef6c4dfe5f1d7448bafaaf3a26f13b5e4eb" -dependencies = [ - "encode_unicode", - "lazy_static", - "libc", - "unicode-width", - "windows-sys 0.52.0", -] - [[package]] name = "crossbeam-channel" version = "0.5.12" @@ -278,12 +265,6 @@ version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "11157ac094ffbdde99aa67b23417ebdd801842852b500e395a45a9c0aac03e4a" -[[package]] -name = "encode_unicode" -version = "0.3.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" - [[package]] name = "equivalent" version = "1.0.1" @@ -447,12 +428,6 @@ dependencies = [ "libc", ] -[[package]] -name = "lazy_static" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" - [[package]] name = "libc" version = "0.2.153" @@ -714,7 +689,6 @@ dependencies = [ "anyhow", "assert_cmd", "clap", - "console", "crossterm", "glob", "notify-debouncer-mini", diff --git a/Cargo.toml b/Cargo.toml index d80550a..da09ba1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -36,7 +36,6 @@ edition.workspace = true [dependencies] anyhow.workspace = true clap = { version = "4.5.4", features = ["derive"] } -console = "0.15.8" crossterm = "0.27.0" notify-debouncer-mini = "0.4.1" ratatui = "0.26.1" diff --git a/src/consts.rs b/src/consts.rs new file mode 100644 index 0000000..40bf150 --- /dev/null +++ b/src/consts.rs @@ -0,0 +1,59 @@ +pub const WELCOME: &str = r" welcome to... + _ _ _ + _ __ _ _ ___| |_| (_)_ __ __ _ ___ + | '__| | | / __| __| | | '_ \ / _` / __| + | | | |_| \__ \ |_| | | | | | (_| \__ \ + |_| \__,_|___/\__|_|_|_| |_|\__, |___/ + |___/"; + +pub const DEFAULT_OUT: &str = + "Is this your first time? Don't worry, Rustlings was made for beginners! We are +going to teach you a lot of things about Rust, but before we can get +started, here's a couple of notes about how Rustlings operates: + +1. The central concept behind Rustlings is that you solve exercises. These + exercises usually have some sort of syntax error in them, which will cause + them to fail compilation or testing. Sometimes there's a logic error instead + of a syntax error. No matter what error, it's your job to find it and fix it! + You'll know when you fixed it because then, the exercise will compile and + Rustlings will be able to move on to the next exercise. +2. If you run Rustlings in watch mode (which we recommend), it'll automatically + start with the first exercise. Don't get confused by an error message popping + up as soon as you run Rustlings! This is part of the exercise that you're + supposed to solve, so open the exercise file in an editor and start your + detective work! +3. If you're stuck on an exercise, there is a helpful hint you can view by typing + 'hint' (in watch mode), or running `rustlings hint exercise_name`. +4. If an exercise doesn't make sense to you, feel free to open an issue on GitHub! + (https://github.com/rust-lang/rustlings/issues/new). We look at every issue, + and sometimes, other learners do too so you can help each other out! + +Got all that? Great! To get started, run `rustlings watch` in order to get the first exercise. +Make sure to have your editor open in the `rustlings` directory!"; + +pub const FENISH_LINE: &str = "+----------------------------------------------------+ +| You made it to the Fe-nish line! | ++-------------------------- ------------------------+ + \\/\x1b[31m + ▒▒ ▒▒▒▒▒▒▒▒ ▒▒▒▒▒▒▒▒ ▒▒ + ▒▒▒▒ ▒▒ ▒▒ ▒▒ ▒▒ ▒▒ ▒▒ ▒▒▒▒ + ▒▒▒▒ ▒▒ ▒▒ ▒▒ ▒▒ ▒▒ ▒▒▒▒ + ░░▒▒▒▒░░▒▒ ▒▒ ▒▒ ▒▒ ▒▒░░▒▒▒▒ + ▓▓▓▓▓▓▓▓ ▓▓ ▓▓██ ▓▓ ▓▓██ ▓▓ ▓▓▓▓▓▓▓▓ + ▒▒▒▒ ▒▒ ████ ▒▒ ████ ▒▒░░ ▒▒▒▒ + ▒▒ ▒▒▒▒▒▒ ▒▒▒▒▒▒ ▒▒▒▒▒▒ ▒▒ + ▒▒▒▒▒▒▒▒▒▒▓▓▓▓▓▓▒▒▒▒▒▒▒▒▓▓▒▒▓▓▒▒▒▒▒▒▒▒ + ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒ + ▒▒▒▒▒▒▒▒▒▒██▒▒▒▒▒▒██▒▒▒▒▒▒▒▒▒▒ + ▒▒ ▒▒▒▒▒▒▒▒▒▒██████▒▒▒▒▒▒▒▒▒▒ ▒▒ + ▒▒ ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒ ▒▒ + ▒▒ ▒▒ ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒ ▒▒ ▒▒ + ▒▒ ▒▒ ▒▒ ▒▒ ▒▒ ▒▒ + ▒▒ ▒▒ ▒▒ ▒▒\x1b[0m + +We hope you enjoyed learning about the various aspects of Rust! +If you noticed any issues, please don't hesitate to report them to our repo. +You can also contribute your own exercises to help the greater community! + +Before reporting an issue or contributing, please read our guidelines: +https://github.com/rust-lang/rustlings/blob/main/CONTRIBUTING.md"; diff --git a/src/main.rs b/src/main.rs index c62837d..47afd01 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,26 +1,22 @@ +use crate::consts::{DEFAULT_OUT, WELCOME}; use crate::embedded::{WriteStrategy, EMBEDDED_FILES}; use crate::exercise::{Exercise, ExerciseList}; use crate::run::run; +use crate::tui::tui; use crate::verify::verify; use anyhow::{bail, Context, Result}; use clap::{Parser, Subcommand}; -use console::Emoji; -use notify_debouncer_mini::notify::RecursiveMode; -use notify_debouncer_mini::{new_debouncer, DebouncedEventKind}; -use std::io::{BufRead, Write}; +use std::io::Write; use std::path::Path; use std::process::exit; -use std::sync::atomic::{AtomicBool, Ordering}; -use std::sync::mpsc::{channel, RecvTimeoutError}; -use std::sync::{Arc, Mutex}; -use std::time::Duration; -use std::{io, thread}; use verify::VerifyState; +mod consts; mod embedded; mod exercise; mod init; mod run; +mod tui; mod verify; /// Rustlings is a collection of small exercises to get you used to writing and reading Rust code @@ -37,7 +33,7 @@ enum Subcommands { Init, /// Verify all exercises according to the recommended order Verify, - /// Rerun `verify` when files were edited + /// Same as just running `rustlings` without a subcommand. Watch, /// Run/Test a single exercise Run { @@ -106,21 +102,20 @@ If you are just starting with Rustlings, run the command `rustlings init` to ini exit(1); } - let command = args.command.unwrap_or_else(|| { - println!("{DEFAULT_OUT}\n"); - exit(0); - }); - - match command { + match args.command { + None | Some(Subcommands::Watch) => { + println!("{DEFAULT_OUT}\n"); + tui(&exercises)?; + } // `Init` is handled above. - Subcommands::Init => (), - Subcommands::List { + Some(Subcommands::Init) => (), + Some(Subcommands::List { paths, names, filter, unsolved, solved, - } => { + }) => { if !paths && !names { println!("{:<17}\t{:<46}\t{:<7}", "Name", "Path", "Status"); } @@ -188,90 +183,30 @@ If you are just starting with Rustlings, run the command `rustlings init` to ini ); exit(0); } - - Subcommands::Run { name } => { + Some(Subcommands::Run { name }) => { let exercise = find_exercise(&name, &exercises)?; run(exercise).unwrap_or_else(|_| exit(1)); } - - Subcommands::Reset { name } => { + Some(Subcommands::Reset { name }) => { let exercise = find_exercise(&name, &exercises)?; EMBEDDED_FILES .write_exercise_to_disk(&exercise.path, WriteStrategy::Overwrite) .with_context(|| format!("Failed to reset the exercise {exercise}"))?; println!("The file {} has been reset!", exercise.path.display()); } - - Subcommands::Hint { name } => { + Some(Subcommands::Hint { name }) => { let exercise = find_exercise(&name, &exercises)?; println!("{}", exercise.hint); } - - Subcommands::Verify => match verify(&exercises, (0, exercises.len()))? { + Some(Subcommands::Verify) => match verify(&exercises, (0, exercises.len()))? { VerifyState::AllExercisesDone => println!("All exercises done!"), VerifyState::Failed(exercise) => bail!("Exercise {exercise} failed"), }, - - Subcommands::Watch => match watch(&exercises) { - Err(e) => { - println!("Error: Could not watch your progress. Error message was {e:?}."); - println!("Most likely you've run out of disk space or your 'inotify limit' has been reached."); - exit(1); - } - Ok(WatchStatus::Finished) => { - println!( - "{emoji} All exercises completed! {emoji}", - emoji = Emoji("🎉", "★") - ); - println!("\n{FENISH_LINE}\n"); - } - Ok(WatchStatus::Unfinished) => { - println!("We hope you're enjoying learning about Rust!"); - println!("If you want to continue working on the exercises at a later point, you can simply run `rustlings watch` again"); - } - }, } Ok(()) } -fn spawn_watch_shell( - failed_exercise_hint: Arc>>, - should_quit: Arc, -) { - println!("Welcome to watch mode! You can type 'help' to get an overview of the commands you can use here."); - - thread::spawn(move || { - let mut input = String::with_capacity(32); - let mut stdin = io::stdin().lock(); - - loop { - // Recycle input buffer. - input.clear(); - - if let Err(e) = stdin.read_line(&mut input) { - println!("error reading command: {e}"); - } - - let input = input.trim(); - if input == "hint" { - if let Some(hint) = &*failed_exercise_hint.lock().unwrap() { - println!("{hint}"); - } - } else if input == "clear" { - println!("\x1B[2J\x1B[1;1H"); - } else if input == "quit" { - should_quit.store(true, Ordering::SeqCst); - println!("Bye!"); - } else if input == "help" { - println!("{WATCH_MODE_HELP_MESSAGE}"); - } else { - println!("unknown command: {input}\n{WATCH_MODE_HELP_MESSAGE}"); - } - } - }); -} - fn find_exercise<'a>(name: &str, exercises: &'a [Exercise]) -> Result<&'a Exercise> { if name == "next" { for exercise in exercises { @@ -290,147 +225,3 @@ fn find_exercise<'a>(name: &str, exercises: &'a [Exercise]) -> Result<&'a Exerci .find(|e| e.name == name) .with_context(|| format!("No exercise found for '{name}'!")) } - -enum WatchStatus { - Finished, - Unfinished, -} - -fn watch(exercises: &[Exercise]) -> Result { - /* Clears the terminal with an ANSI escape code. - Works in UNIX and newer Windows terminals. */ - fn clear_screen() { - println!("\x1Bc"); - } - - let (tx, rx) = channel(); - let should_quit = Arc::new(AtomicBool::new(false)); - - let mut debouncer = new_debouncer(Duration::from_secs(1), tx)?; - debouncer - .watcher() - .watch(Path::new("exercises"), RecursiveMode::Recursive)?; - - clear_screen(); - - let failed_exercise_hint = match verify(exercises, (0, exercises.len()))? { - VerifyState::AllExercisesDone => return Ok(WatchStatus::Finished), - VerifyState::Failed(exercise) => Arc::new(Mutex::new(Some(exercise.hint.clone()))), - }; - - spawn_watch_shell(Arc::clone(&failed_exercise_hint), Arc::clone(&should_quit)); - - let mut pending_exercises = Vec::with_capacity(exercises.len()); - loop { - match rx.recv_timeout(Duration::from_secs(1)) { - Ok(event) => match event { - Ok(events) => { - for event in events { - if event.kind == DebouncedEventKind::Any - && event.path.extension().is_some_and(|ext| ext == "rs") - { - pending_exercises.extend(exercises.iter().filter(|exercise| { - !exercise.looks_done().unwrap_or(false) - || event.path.ends_with(&exercise.path) - })); - let num_done = exercises.len() - pending_exercises.len(); - - clear_screen(); - - match verify( - pending_exercises.iter().copied(), - (num_done, exercises.len()), - )? { - VerifyState::AllExercisesDone => return Ok(WatchStatus::Finished), - VerifyState::Failed(exercise) => { - let hint = exercise.hint.clone(); - *failed_exercise_hint.lock().unwrap() = Some(hint); - } - } - - pending_exercises.clear(); - } - } - } - Err(e) => println!("watch error: {e:?}"), - }, - Err(RecvTimeoutError::Timeout) => { - // the timeout expired, just check the `should_quit` variable below then loop again - } - Err(e) => println!("watch error: {e:?}"), - } - // Check if we need to exit - if should_quit.load(Ordering::SeqCst) { - return Ok(WatchStatus::Unfinished); - } - } -} - -const WELCOME: &str = r" welcome to... - _ _ _ - _ __ _ _ ___| |_| (_)_ __ __ _ ___ - | '__| | | / __| __| | | '_ \ / _` / __| - | | | |_| \__ \ |_| | | | | | (_| \__ \ - |_| \__,_|___/\__|_|_|_| |_|\__, |___/ - |___/"; - -const DEFAULT_OUT: &str = - "Is this your first time? Don't worry, Rustlings was made for beginners! We are -going to teach you a lot of things about Rust, but before we can get -started, here's a couple of notes about how Rustlings operates: - -1. The central concept behind Rustlings is that you solve exercises. These - exercises usually have some sort of syntax error in them, which will cause - them to fail compilation or testing. Sometimes there's a logic error instead - of a syntax error. No matter what error, it's your job to find it and fix it! - You'll know when you fixed it because then, the exercise will compile and - Rustlings will be able to move on to the next exercise. -2. If you run Rustlings in watch mode (which we recommend), it'll automatically - start with the first exercise. Don't get confused by an error message popping - up as soon as you run Rustlings! This is part of the exercise that you're - supposed to solve, so open the exercise file in an editor and start your - detective work! -3. If you're stuck on an exercise, there is a helpful hint you can view by typing - 'hint' (in watch mode), or running `rustlings hint exercise_name`. -4. If an exercise doesn't make sense to you, feel free to open an issue on GitHub! - (https://github.com/rust-lang/rustlings/issues/new). We look at every issue, - and sometimes, other learners do too so you can help each other out! - -Got all that? Great! To get started, run `rustlings watch` in order to get the first exercise. -Make sure to have your editor open in the `rustlings` directory!"; - -const WATCH_MODE_HELP_MESSAGE: &str = "Commands available to you in watch mode: - hint - prints the current exercise's hint - clear - clears the screen - quit - quits watch mode - help - displays this help message - -Watch mode automatically re-evaluates the current exercise -when you edit a file's contents."; - -const FENISH_LINE: &str = "+----------------------------------------------------+ -| You made it to the Fe-nish line! | -+-------------------------- ------------------------+ - \\/\x1b[31m - ▒▒ ▒▒▒▒▒▒▒▒ ▒▒▒▒▒▒▒▒ ▒▒ - ▒▒▒▒ ▒▒ ▒▒ ▒▒ ▒▒ ▒▒ ▒▒ ▒▒▒▒ - ▒▒▒▒ ▒▒ ▒▒ ▒▒ ▒▒ ▒▒ ▒▒▒▒ - ░░▒▒▒▒░░▒▒ ▒▒ ▒▒ ▒▒ ▒▒░░▒▒▒▒ - ▓▓▓▓▓▓▓▓ ▓▓ ▓▓██ ▓▓ ▓▓██ ▓▓ ▓▓▓▓▓▓▓▓ - ▒▒▒▒ ▒▒ ████ ▒▒ ████ ▒▒░░ ▒▒▒▒ - ▒▒ ▒▒▒▒▒▒ ▒▒▒▒▒▒ ▒▒▒▒▒▒ ▒▒ - ▒▒▒▒▒▒▒▒▒▒▓▓▓▓▓▓▒▒▒▒▒▒▒▒▓▓▒▒▓▓▒▒▒▒▒▒▒▒ - ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒ - ▒▒▒▒▒▒▒▒▒▒██▒▒▒▒▒▒██▒▒▒▒▒▒▒▒▒▒ - ▒▒ ▒▒▒▒▒▒▒▒▒▒██████▒▒▒▒▒▒▒▒▒▒ ▒▒ - ▒▒ ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒ ▒▒ - ▒▒ ▒▒ ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒ ▒▒ ▒▒ - ▒▒ ▒▒ ▒▒ ▒▒ ▒▒ ▒▒ - ▒▒ ▒▒ ▒▒ ▒▒\x1b[0m - -We hope you enjoyed learning about the various aspects of Rust! -If you noticed any issues, please don't hesitate to report them to our repo. -You can also contribute your own exercises to help the greater community! - -Before reporting an issue or contributing, please read our guidelines: -https://github.com/rust-lang/rustlings/blob/main/CONTRIBUTING.md"; diff --git a/src/tui.rs b/src/tui.rs new file mode 100644 index 0000000..bb87365 --- /dev/null +++ b/src/tui.rs @@ -0,0 +1,92 @@ +use anyhow::Result; +use crossterm::{ + terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen}, + ExecutableCommand, +}; +use notify_debouncer_mini::{new_debouncer, notify::RecursiveMode, DebouncedEventKind}; +use ratatui::{backend::CrosstermBackend, Terminal}; +use std::{ + io::stdout, + path::Path, + sync::mpsc::{channel, RecvTimeoutError}, + time::Duration, +}; + +use crate::{ + exercise::Exercise, + verify::{verify, VerifyState}, +}; + +fn watch(exercises: &[Exercise]) -> Result<()> { + let (tx, rx) = channel(); + + let mut debouncer = new_debouncer(Duration::from_secs(1), tx)?; + debouncer + .watcher() + .watch(Path::new("exercises"), RecursiveMode::Recursive)?; + + let mut failed_exercise_hint = match verify(exercises, (0, exercises.len()))? { + VerifyState::AllExercisesDone => return Ok(()), + VerifyState::Failed(exercise) => Some(&exercise.hint), + }; + + let mut pending_exercises = Vec::with_capacity(exercises.len()); + loop { + match rx.recv_timeout(Duration::from_secs(1)) { + Ok(event) => match event { + Ok(events) => { + for event in events { + if event.kind == DebouncedEventKind::Any + && event.path.extension().is_some_and(|ext| ext == "rs") + { + pending_exercises.extend(exercises.iter().filter(|exercise| { + !exercise.looks_done().unwrap_or(false) + || event.path.ends_with(&exercise.path) + })); + let num_done = exercises.len() - pending_exercises.len(); + + match verify( + pending_exercises.iter().copied(), + (num_done, exercises.len()), + )? { + VerifyState::AllExercisesDone => return Ok(()), + VerifyState::Failed(exercise) => { + failed_exercise_hint = Some(&exercise.hint); + } + } + + pending_exercises.clear(); + } + } + } + Err(e) => println!("watch error: {e:?}"), + }, + Err(RecvTimeoutError::Timeout) => { + // the timeout expired, just check the `should_quit` variable below then loop again + } + Err(e) => println!("watch error: {e:?}"), + } + + // TODO: Check if we need to exit + } +} + +pub fn tui(exercises: &[Exercise]) -> Result<()> { + let mut stdout = stdout().lock(); + stdout.execute(EnterAlternateScreen)?; + enable_raw_mode()?; + let mut terminal = Terminal::new(CrosstermBackend::new(&mut stdout))?; + terminal.clear()?; + + watch(exercises)?; + + drop(terminal); + stdout.execute(LeaveAlternateScreen)?; + disable_raw_mode()?; + + // TODO + println!("We hope you're enjoying learning about Rust!"); + println!("If you want to continue working on the exercises at a later point, you can simply run `rustlings watch` again"); + + Ok(()) +} diff --git a/src/verify.rs b/src/verify.rs index 5beb206..aec2185 100644 --- a/src/verify.rs +++ b/src/verify.rs @@ -1,5 +1,5 @@ use anyhow::Result; -use console::style; +use crossterm::style::{Attribute, ContentStyle, Stylize}; use std::io::{stdout, Write}; use crate::exercise::{Exercise, Mode, State}; @@ -50,20 +50,26 @@ pub fn verify<'a>( println!( "\nYou can keep working on this exercise, or jump into the next one by removing the {} comment:\n", - style("`I AM NOT DONE`").bold() + "`I AM NOT DONE`".bold() ); for context_line in context { let formatted_line = if context_line.important { - format!("{}", style(context_line.line).bold()) + format!("{}", context_line.line.bold()) } else { context_line.line }; println!( "{:>2} {} {}", - style(context_line.number).blue().bold(), - style("|").blue(), + ContentStyle { + foreground_color: Some(crossterm::style::Color::Blue), + background_color: None, + underline_color: None, + attributes: Attribute::Bold.into() + } + .apply(context_line.number), + "|".blue(), formatted_line, ); } -- cgit v1.2.3 From 729385362c06da0c90015bb2d4b6b341d2cd489b Mon Sep 17 00:00:00 2001 From: mo8it Date: Sun, 7 Apr 2024 03:03:59 +0200 Subject: Update deps --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'Cargo.lock') diff --git a/Cargo.lock b/Cargo.lock index 33d3030..ee46943 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -711,9 +711,9 @@ dependencies = [ [[package]] name = "rustversion" -version = "1.0.14" +version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4" +checksum = "80af6f9131f277a45a3fba6ce8e2258037bb0477a67e610d3c1fe046ab31de47" [[package]] name = "ryu" -- cgit v1.2.3 From fa1f239a702eb2c0b7e0115e986481156961bbc8 Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 11 Apr 2024 02:51:02 +0200 Subject: Remove "I AM NOT DONE" and the verify mode and add AppState --- Cargo.lock | 1 - Cargo.toml | 1 - README.md | 8 +- exercises/00_intro/intro1.rs | 4 +- exercises/00_intro/intro2.rs | 2 - exercises/01_variables/variables1.rs | 2 - exercises/01_variables/variables2.rs | 2 - exercises/01_variables/variables3.rs | 2 - exercises/01_variables/variables4.rs | 2 - exercises/01_variables/variables5.rs | 2 - exercises/01_variables/variables6.rs | 2 - exercises/02_functions/functions1.rs | 2 - exercises/02_functions/functions2.rs | 2 - exercises/02_functions/functions3.rs | 2 - exercises/02_functions/functions4.rs | 2 - exercises/02_functions/functions5.rs | 2 - exercises/03_if/if1.rs | 2 - exercises/03_if/if2.rs | 2 - exercises/03_if/if3.rs | 2 - exercises/04_primitive_types/primitive_types1.rs | 2 - exercises/04_primitive_types/primitive_types2.rs | 2 - exercises/04_primitive_types/primitive_types3.rs | 2 - exercises/04_primitive_types/primitive_types4.rs | 2 - exercises/04_primitive_types/primitive_types5.rs | 2 - exercises/04_primitive_types/primitive_types6.rs | 2 - exercises/05_vecs/vecs1.rs | 2 - exercises/05_vecs/vecs2.rs | 2 - exercises/06_move_semantics/move_semantics1.rs | 2 - exercises/06_move_semantics/move_semantics2.rs | 2 - exercises/06_move_semantics/move_semantics3.rs | 2 - exercises/06_move_semantics/move_semantics4.rs | 2 - exercises/06_move_semantics/move_semantics5.rs | 2 - exercises/06_move_semantics/move_semantics6.rs | 2 - exercises/07_structs/structs1.rs | 2 - exercises/07_structs/structs2.rs | 2 - exercises/07_structs/structs3.rs | 2 - exercises/08_enums/enums1.rs | 2 - exercises/08_enums/enums2.rs | 2 - exercises/08_enums/enums3.rs | 2 - exercises/09_strings/strings1.rs | 2 - exercises/09_strings/strings2.rs | 2 - exercises/09_strings/strings3.rs | 2 - exercises/09_strings/strings4.rs | 2 - exercises/10_modules/modules1.rs | 2 - exercises/10_modules/modules2.rs | 2 - exercises/10_modules/modules3.rs | 2 - exercises/11_hashmaps/hashmaps1.rs | 2 - exercises/11_hashmaps/hashmaps2.rs | 2 - exercises/11_hashmaps/hashmaps3.rs | 2 - exercises/12_options/options1.rs | 2 - exercises/12_options/options2.rs | 2 - exercises/12_options/options3.rs | 2 - exercises/13_error_handling/errors1.rs | 2 - exercises/13_error_handling/errors2.rs | 2 - exercises/13_error_handling/errors3.rs | 2 - exercises/13_error_handling/errors4.rs | 2 - exercises/13_error_handling/errors5.rs | 2 - exercises/13_error_handling/errors6.rs | 2 - exercises/14_generics/generics1.rs | 2 - exercises/14_generics/generics2.rs | 2 - exercises/15_traits/traits1.rs | 2 - exercises/15_traits/traits2.rs | 2 - exercises/15_traits/traits3.rs | 2 - exercises/15_traits/traits4.rs | 2 - exercises/15_traits/traits5.rs | 2 - exercises/16_lifetimes/lifetimes1.rs | 2 - exercises/16_lifetimes/lifetimes2.rs | 2 - exercises/16_lifetimes/lifetimes3.rs | 2 - exercises/17_tests/tests1.rs | 2 - exercises/17_tests/tests2.rs | 2 - exercises/17_tests/tests3.rs | 2 - exercises/17_tests/tests4.rs | 2 - exercises/18_iterators/iterators1.rs | 2 - exercises/18_iterators/iterators2.rs | 2 - exercises/18_iterators/iterators3.rs | 2 - exercises/18_iterators/iterators4.rs | 2 - exercises/18_iterators/iterators5.rs | 2 - exercises/19_smart_pointers/arc1.rs | 2 - exercises/19_smart_pointers/box1.rs | 2 - exercises/19_smart_pointers/cow1.rs | 2 - exercises/19_smart_pointers/rc1.rs | 2 - exercises/20_threads/threads1.rs | 2 - exercises/20_threads/threads2.rs | 2 - exercises/20_threads/threads3.rs | 2 - exercises/21_macros/macros1.rs | 2 - exercises/21_macros/macros2.rs | 2 - exercises/21_macros/macros3.rs | 2 - exercises/21_macros/macros4.rs | 2 - exercises/22_clippy/clippy1.rs | 2 - exercises/22_clippy/clippy2.rs | 2 - exercises/22_clippy/clippy3.rs | 2 - exercises/23_conversions/as_ref_mut.rs | 2 - exercises/23_conversions/from_into.rs | 2 - exercises/23_conversions/from_str.rs | 2 - exercises/23_conversions/try_from_into.rs | 2 - exercises/23_conversions/using_as.rs | 2 - exercises/quiz1.rs | 2 - exercises/quiz2.rs | 2 - exercises/quiz3.rs | 2 - info.toml | 7 +- src/app_state.rs | 185 ++++++++++++++++++ src/exercise.rs | 206 +-------------------- src/list.rs | 21 +-- src/list/state.rs | 71 +++---- src/main.rs | 67 +++---- src/run.rs | 23 ++- src/state_file.rs | 68 ------- src/verify.rs | 85 --------- src/watch.rs | 10 +- src/watch/state.rs | 96 +++------- tests/fixture/state/exercises/pending_exercise.rs | 2 - .../state/exercises/pending_test_exercise.rs | 2 - tests/integration_tests.rs | 28 +-- 113 files changed, 306 insertions(+), 769 deletions(-) create mode 100644 src/app_state.rs delete mode 100644 src/state_file.rs delete mode 100644 src/verify.rs (limited to 'Cargo.lock') diff --git a/Cargo.lock b/Cargo.lock index ee46943..aeb6c61 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -699,7 +699,6 @@ dependencies = [ "serde_json", "toml_edit", "which", - "winnow", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index da09ba1..435dfd4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -44,7 +44,6 @@ serde_json = "1.0.115" serde.workspace = true toml_edit.workspace = true which = "6.0.1" -winnow = "0.6.5" [dev-dependencies] assert_cmd = "2.0.14" diff --git a/README.md b/README.md index 6b9c983..fd76fdf 100644 --- a/README.md +++ b/README.md @@ -101,13 +101,7 @@ The task is simple. Most exercises contain an error that keeps them from compili rustlings watch ``` -This will try to verify the completion of every exercise in a predetermined order (what we think is best for newcomers). It will also rerun automatically every time you change a file in the `exercises/` directory. If you want to only run it once, you can use: - -```bash -rustlings verify -``` - -This will do the same as watch, but it'll quit after running. +This will try to verify the completion of every exercise in a predetermined order (what we think is best for newcomers). It will also rerun automatically every time you change a file in the `exercises/` directory. In case you want to go by your own order, or want to only verify a single exercise, you can run: diff --git a/exercises/00_intro/intro1.rs b/exercises/00_intro/intro1.rs index 5dd18b4..aa505a1 100644 --- a/exercises/00_intro/intro1.rs +++ b/exercises/00_intro/intro1.rs @@ -1,6 +1,6 @@ // intro1.rs // -// About this `I AM NOT DONE` thing: +// TODO: Update comment // 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, remove the `I AM NOT DONE` comment below. @@ -13,8 +13,6 @@ // Execute `rustlings hint intro1` or use the `hint` watch subcommand for a // hint. -// I AM NOT DONE - fn main() { println!("Hello and"); println!(r#" welcome to... "#); diff --git a/exercises/00_intro/intro2.rs b/exercises/00_intro/intro2.rs index a28ad3d..84e0d75 100644 --- a/exercises/00_intro/intro2.rs +++ b/exercises/00_intro/intro2.rs @@ -5,8 +5,6 @@ // Execute `rustlings hint intro2` or use the `hint` watch subcommand for a // hint. -// I AM NOT DONE - fn main() { printline!("Hello there!") } diff --git a/exercises/01_variables/variables1.rs b/exercises/01_variables/variables1.rs index b3e089a..56408f3 100644 --- a/exercises/01_variables/variables1.rs +++ b/exercises/01_variables/variables1.rs @@ -5,8 +5,6 @@ // Execute `rustlings hint variables1` or use the `hint` watch subcommand for a // hint. -// I AM NOT DONE - fn main() { x = 5; println!("x has the value {}", x); diff --git a/exercises/01_variables/variables2.rs b/exercises/01_variables/variables2.rs index e1c23ed..0f417e0 100644 --- a/exercises/01_variables/variables2.rs +++ b/exercises/01_variables/variables2.rs @@ -3,8 +3,6 @@ // Execute `rustlings hint variables2` or use the `hint` watch subcommand for a // hint. -// I AM NOT DONE - fn main() { let x; if x == 10 { diff --git a/exercises/01_variables/variables3.rs b/exercises/01_variables/variables3.rs index 86bed41..421c6b1 100644 --- a/exercises/01_variables/variables3.rs +++ b/exercises/01_variables/variables3.rs @@ -3,8 +3,6 @@ // Execute `rustlings hint variables3` or use the `hint` watch subcommand for a // hint. -// I AM NOT DONE - fn main() { let x: i32; println!("Number {}", x); diff --git a/exercises/01_variables/variables4.rs b/exercises/01_variables/variables4.rs index 5394f39..68f8f50 100644 --- a/exercises/01_variables/variables4.rs +++ b/exercises/01_variables/variables4.rs @@ -3,8 +3,6 @@ // Execute `rustlings hint variables4` or use the `hint` watch subcommand for a // hint. -// I AM NOT DONE - fn main() { let x = 3; println!("Number {}", x); diff --git a/exercises/01_variables/variables5.rs b/exercises/01_variables/variables5.rs index a29b38b..7014c56 100644 --- a/exercises/01_variables/variables5.rs +++ b/exercises/01_variables/variables5.rs @@ -3,8 +3,6 @@ // Execute `rustlings hint variables5` or use the `hint` watch subcommand for a // hint. -// I AM NOT DONE - fn main() { let number = "T-H-R-E-E"; // don't change this line println!("Spell a Number : {}", number); diff --git a/exercises/01_variables/variables6.rs b/exercises/01_variables/variables6.rs index 853183b..9f47682 100644 --- a/exercises/01_variables/variables6.rs +++ b/exercises/01_variables/variables6.rs @@ -3,8 +3,6 @@ // Execute `rustlings hint variables6` or use the `hint` watch subcommand for a // hint. -// I AM NOT DONE - const NUMBER = 3; fn main() { println!("Number {}", NUMBER); diff --git a/exercises/02_functions/functions1.rs b/exercises/02_functions/functions1.rs index 40ed9a0..2365f91 100644 --- a/exercises/02_functions/functions1.rs +++ b/exercises/02_functions/functions1.rs @@ -3,8 +3,6 @@ // Execute `rustlings hint functions1` or use the `hint` watch subcommand for a // hint. -// I AM NOT DONE - fn main() { call_me(); } diff --git a/exercises/02_functions/functions2.rs b/exercises/02_functions/functions2.rs index 5154f34..64dbd66 100644 --- a/exercises/02_functions/functions2.rs +++ b/exercises/02_functions/functions2.rs @@ -3,8 +3,6 @@ // Execute `rustlings hint functions2` or use the `hint` watch subcommand for a // hint. -// I AM NOT DONE - fn main() { call_me(3); } diff --git a/exercises/02_functions/functions3.rs b/exercises/02_functions/functions3.rs index 74f44d6..5037121 100644 --- a/exercises/02_functions/functions3.rs +++ b/exercises/02_functions/functions3.rs @@ -3,8 +3,6 @@ // Execute `rustlings hint functions3` or use the `hint` watch subcommand for a // hint. -// I AM NOT DONE - fn main() { call_me(); } diff --git a/exercises/02_functions/functions4.rs b/exercises/02_functions/functions4.rs index 77c4b2a..6b449ed 100644 --- a/exercises/02_functions/functions4.rs +++ b/exercises/02_functions/functions4.rs @@ -8,8 +8,6 @@ // Execute `rustlings hint functions4` or use the `hint` watch subcommand for a // hint. -// I AM NOT DONE - fn main() { let original_price = 51; println!("Your sale price is {}", sale_price(original_price)); diff --git a/exercises/02_functions/functions5.rs b/exercises/02_functions/functions5.rs index f1b63f4..0c96322 100644 --- a/exercises/02_functions/functions5.rs +++ b/exercises/02_functions/functions5.rs @@ -3,8 +3,6 @@ // Execute `rustlings hint functions5` or use the `hint` watch subcommand for a // hint. -// I AM NOT DONE - fn main() { let answer = square(3); println!("The square of 3 is {}", answer); diff --git a/exercises/03_if/if1.rs b/exercises/03_if/if1.rs index d2afccf..a1df66b 100644 --- a/exercises/03_if/if1.rs +++ b/exercises/03_if/if1.rs @@ -2,8 +2,6 @@ // // Execute `rustlings hint if1` or use the `hint` watch subcommand for a hint. -// I AM NOT DONE - pub fn bigger(a: i32, b: i32) -> i32 { // Complete this function to return the bigger number! // If both numbers are equal, any of them can be returned. diff --git a/exercises/03_if/if2.rs b/exercises/03_if/if2.rs index f512f13..7b9c05f 100644 --- a/exercises/03_if/if2.rs +++ b/exercises/03_if/if2.rs @@ -5,8 +5,6 @@ // // Execute `rustlings hint if2` or use the `hint` watch subcommand for a hint. -// I AM NOT DONE - pub fn foo_if_fizz(fizzish: &str) -> &str { if fizzish == "fizz" { "foo" diff --git a/exercises/03_if/if3.rs b/exercises/03_if/if3.rs index 1696274..caba172 100644 --- a/exercises/03_if/if3.rs +++ b/exercises/03_if/if3.rs @@ -2,8 +2,6 @@ // // Execute `rustlings hint if3` or use the `hint` watch subcommand for a hint. -// I AM NOT DONE - pub fn animal_habitat(animal: &str) -> &'static str { let identifier = if animal == "crab" { 1 diff --git a/exercises/04_primitive_types/primitive_types1.rs b/exercises/04_primitive_types/primitive_types1.rs index 3663340..f9169c8 100644 --- a/exercises/04_primitive_types/primitive_types1.rs +++ b/exercises/04_primitive_types/primitive_types1.rs @@ -3,8 +3,6 @@ // Fill in the rest of the line that has code missing! No hints, there's no // tricks, just get used to typing these :) -// I AM NOT DONE - fn main() { // Booleans (`bool`) diff --git a/exercises/04_primitive_types/primitive_types2.rs b/exercises/04_primitive_types/primitive_types2.rs index f1616ed..1911b12 100644 --- a/exercises/04_primitive_types/primitive_types2.rs +++ b/exercises/04_primitive_types/primitive_types2.rs @@ -3,8 +3,6 @@ // Fill in the rest of the line that has code missing! No hints, there's no // tricks, just get used to typing these :) -// I AM NOT DONE - fn main() { // Characters (`char`) diff --git a/exercises/04_primitive_types/primitive_types3.rs b/exercises/04_primitive_types/primitive_types3.rs index 8b0de44..70a8cc2 100644 --- a/exercises/04_primitive_types/primitive_types3.rs +++ b/exercises/04_primitive_types/primitive_types3.rs @@ -5,8 +5,6 @@ // Execute `rustlings hint primitive_types3` or use the `hint` watch subcommand // for a hint. -// I AM NOT DONE - fn main() { let a = ??? diff --git a/exercises/04_primitive_types/primitive_types4.rs b/exercises/04_primitive_types/primitive_types4.rs index d44d877..8ed0a82 100644 --- a/exercises/04_primitive_types/primitive_types4.rs +++ b/exercises/04_primitive_types/primitive_types4.rs @@ -5,8 +5,6 @@ // Execute `rustlings hint primitive_types4` or use the `hint` watch subcommand // for a hint. -// I AM NOT DONE - #[test] fn slice_out_of_array() { let a = [1, 2, 3, 4, 5]; diff --git a/exercises/04_primitive_types/primitive_types5.rs b/exercises/04_primitive_types/primitive_types5.rs index f646986..5754a3d 100644 --- a/exercises/04_primitive_types/primitive_types5.rs +++ b/exercises/04_primitive_types/primitive_types5.rs @@ -5,8 +5,6 @@ // Execute `rustlings hint primitive_types5` or use the `hint` watch subcommand // for a hint. -// I AM NOT DONE - fn main() { let cat = ("Furry McFurson", 3.5); let /* your pattern here */ = cat; diff --git a/exercises/04_primitive_types/primitive_types6.rs b/exercises/04_primitive_types/primitive_types6.rs index 07cc46c..5f82f10 100644 --- a/exercises/04_primitive_types/primitive_types6.rs +++ b/exercises/04_primitive_types/primitive_types6.rs @@ -6,8 +6,6 @@ // Execute `rustlings hint primitive_types6` or use the `hint` watch subcommand // for a hint. -// I AM NOT DONE - #[test] fn indexing_tuple() { let numbers = (1, 2, 3); diff --git a/exercises/05_vecs/vecs1.rs b/exercises/05_vecs/vecs1.rs index 65b7a7f..c64acbb 100644 --- a/exercises/05_vecs/vecs1.rs +++ b/exercises/05_vecs/vecs1.rs @@ -7,8 +7,6 @@ // // Execute `rustlings hint vecs1` or use the `hint` watch subcommand for a hint. -// I AM NOT DONE - fn array_and_vec() -> ([i32; 4], Vec) { let a = [10, 20, 30, 40]; // a plain array let v = // TODO: declare your vector here with the macro for vectors diff --git a/exercises/05_vecs/vecs2.rs b/exercises/05_vecs/vecs2.rs index e92c970..d64d3d1 100644 --- a/exercises/05_vecs/vecs2.rs +++ b/exercises/05_vecs/vecs2.rs @@ -7,8 +7,6 @@ // // Execute `rustlings hint vecs2` or use the `hint` watch subcommand for a hint. -// I AM NOT DONE - fn vec_loop(mut v: Vec) -> Vec { for element in v.iter_mut() { // TODO: Fill this up so that each element in the Vec `v` is diff --git a/exercises/06_move_semantics/move_semantics1.rs b/exercises/06_move_semantics/move_semantics1.rs index e063937..c612ba9 100644 --- a/exercises/06_move_semantics/move_semantics1.rs +++ b/exercises/06_move_semantics/move_semantics1.rs @@ -3,8 +3,6 @@ // Execute `rustlings hint move_semantics1` or use the `hint` watch subcommand // for a hint. -// I AM NOT DONE - #[test] fn main() { let vec0 = vec![22, 44, 66]; diff --git a/exercises/06_move_semantics/move_semantics2.rs b/exercises/06_move_semantics/move_semantics2.rs index dc58be5..3457d11 100644 --- a/exercises/06_move_semantics/move_semantics2.rs +++ b/exercises/06_move_semantics/move_semantics2.rs @@ -5,8 +5,6 @@ // Execute `rustlings hint move_semantics2` or use the `hint` watch subcommand // for a hint. -// I AM NOT DONE - #[test] fn main() { let vec0 = vec![22, 44, 66]; diff --git a/exercises/06_move_semantics/move_semantics3.rs b/exercises/06_move_semantics/move_semantics3.rs index 7152c71..9415eb1 100644 --- a/exercises/06_move_semantics/move_semantics3.rs +++ b/exercises/06_move_semantics/move_semantics3.rs @@ -6,8 +6,6 @@ // Execute `rustlings hint move_semantics3` or use the `hint` watch subcommand // for a hint. -// I AM NOT DONE - #[test] fn main() { let vec0 = vec![22, 44, 66]; diff --git a/exercises/06_move_semantics/move_semantics4.rs b/exercises/06_move_semantics/move_semantics4.rs index bfc917f..1509f5d 100644 --- a/exercises/06_move_semantics/move_semantics4.rs +++ b/exercises/06_move_semantics/move_semantics4.rs @@ -7,8 +7,6 @@ // Execute `rustlings hint move_semantics4` or use the `hint` watch subcommand // for a hint. -// I AM NOT DONE - #[test] fn main() { let vec0 = vec![22, 44, 66]; diff --git a/exercises/06_move_semantics/move_semantics5.rs b/exercises/06_move_semantics/move_semantics5.rs index 267bdcc..c84d2fe 100644 --- a/exercises/06_move_semantics/move_semantics5.rs +++ b/exercises/06_move_semantics/move_semantics5.rs @@ -6,8 +6,6 @@ // Execute `rustlings hint move_semantics5` or use the `hint` watch subcommand // for a hint. -// I AM NOT DONE - #[test] fn main() { let mut x = 100; diff --git a/exercises/06_move_semantics/move_semantics6.rs b/exercises/06_move_semantics/move_semantics6.rs index cace4ca..6059e61 100644 --- a/exercises/06_move_semantics/move_semantics6.rs +++ b/exercises/06_move_semantics/move_semantics6.rs @@ -5,8 +5,6 @@ // Execute `rustlings hint move_semantics6` or use the `hint` watch subcommand // for a hint. -// I AM NOT DONE - fn main() { let data = "Rust is great!".to_string(); diff --git a/exercises/07_structs/structs1.rs b/exercises/07_structs/structs1.rs index 5fa5821..2978121 100644 --- a/exercises/07_structs/structs1.rs +++ b/exercises/07_structs/structs1.rs @@ -5,8 +5,6 @@ // Execute `rustlings hint structs1` or use the `hint` watch subcommand for a // hint. -// I AM NOT DONE - struct ColorClassicStruct { // TODO: Something goes here } diff --git a/exercises/07_structs/structs2.rs b/exercises/07_structs/structs2.rs index 328567f..a7a2dec 100644 --- a/exercises/07_structs/structs2.rs +++ b/exercises/07_structs/structs2.rs @@ -5,8 +5,6 @@ // Execute `rustlings hint structs2` or use the `hint` watch subcommand for a // hint. -// I AM NOT DONE - #[derive(Debug)] struct Order { name: String, diff --git a/exercises/07_structs/structs3.rs b/exercises/07_structs/structs3.rs index 7cda5af..9835b81 100644 --- a/exercises/07_structs/structs3.rs +++ b/exercises/07_structs/structs3.rs @@ -7,8 +7,6 @@ // Execute `rustlings hint structs3` or use the `hint` watch subcommand for a // hint. -// I AM NOT DONE - #[derive(Debug)] struct Package { sender_country: String, diff --git a/exercises/08_enums/enums1.rs b/exercises/08_enums/enums1.rs index 25525b2..330269c 100644 --- a/exercises/08_enums/enums1.rs +++ b/exercises/08_enums/enums1.rs @@ -2,8 +2,6 @@ // // No hints this time! ;) -// I AM NOT DONE - #[derive(Debug)] enum Message { // TODO: define a few types of messages as used below diff --git a/exercises/08_enums/enums2.rs b/exercises/08_enums/enums2.rs index df93fe0..f0e4e6d 100644 --- a/exercises/08_enums/enums2.rs +++ b/exercises/08_enums/enums2.rs @@ -3,8 +3,6 @@ // Execute `rustlings hint enums2` or use the `hint` watch subcommand for a // hint. -// I AM NOT DONE - #[derive(Debug)] enum Message { // TODO: define the different variants used below diff --git a/exercises/08_enums/enums3.rs b/exercises/08_enums/enums3.rs index 92d18c4..580a553 100644 --- a/exercises/08_enums/enums3.rs +++ b/exercises/08_enums/enums3.rs @@ -5,8 +5,6 @@ // Execute `rustlings hint enums3` or use the `hint` watch subcommand for a // hint. -// I AM NOT DONE - enum Message { // TODO: implement the message variant types based on their usage below } diff --git a/exercises/09_strings/strings1.rs b/exercises/09_strings/strings1.rs index f50e1fa..a1255a3 100644 --- a/exercises/09_strings/strings1.rs +++ b/exercises/09_strings/strings1.rs @@ -5,8 +5,6 @@ // Execute `rustlings hint strings1` or use the `hint` watch subcommand for a // hint. -// I AM NOT DONE - fn main() { let answer = current_favorite_color(); println!("My current favorite color is {}", answer); diff --git a/exercises/09_strings/strings2.rs b/exercises/09_strings/strings2.rs index 4d95d16..ba76fe6 100644 --- a/exercises/09_strings/strings2.rs +++ b/exercises/09_strings/strings2.rs @@ -5,8 +5,6 @@ // Execute `rustlings hint strings2` or use the `hint` watch subcommand for a // hint. -// I AM NOT DONE - fn main() { let word = String::from("green"); // Try not changing this line :) if is_a_color_word(word) { diff --git a/exercises/09_strings/strings3.rs b/exercises/09_strings/strings3.rs index 384e7ce..dedc081 100644 --- a/exercises/09_strings/strings3.rs +++ b/exercises/09_strings/strings3.rs @@ -3,8 +3,6 @@ // Execute `rustlings hint strings3` or use the `hint` watch subcommand for a // hint. -// I AM NOT DONE - fn trim_me(input: &str) -> String { // TODO: Remove whitespace from both ends of a string! ??? diff --git a/exercises/09_strings/strings4.rs b/exercises/09_strings/strings4.rs index e8c54ac..a034aa4 100644 --- a/exercises/09_strings/strings4.rs +++ b/exercises/09_strings/strings4.rs @@ -7,8 +7,6 @@ // // No hints this time! -// I AM NOT DONE - fn string_slice(arg: &str) { println!("{}", arg); } diff --git a/exercises/10_modules/modules1.rs b/exercises/10_modules/modules1.rs index 9eb5a48..c750946 100644 --- a/exercises/10_modules/modules1.rs +++ b/exercises/10_modules/modules1.rs @@ -3,8 +3,6 @@ // Execute `rustlings hint modules1` or use the `hint` watch subcommand for a // hint. -// I AM NOT DONE - mod sausage_factory { // Don't let anybody outside of this module see this! fn get_secret_recipe() -> String { diff --git a/exercises/10_modules/modules2.rs b/exercises/10_modules/modules2.rs index 0415454..4d3106c 100644 --- a/exercises/10_modules/modules2.rs +++ b/exercises/10_modules/modules2.rs @@ -7,8 +7,6 @@ // Execute `rustlings hint modules2` or use the `hint` watch subcommand for a // hint. -// I AM NOT DONE - mod delicious_snacks { // TODO: Fix these use statements use self::fruits::PEAR as ??? diff --git a/exercises/10_modules/modules3.rs b/exercises/10_modules/modules3.rs index f2bb050..c211a76 100644 --- a/exercises/10_modules/modules3.rs +++ b/exercises/10_modules/modules3.rs @@ -8,8 +8,6 @@ // Execute `rustlings hint modules3` or use the `hint` watch subcommand for a // hint. -// I AM NOT DONE - // TODO: Complete this use statement use ??? diff --git a/exercises/11_hashmaps/hashmaps1.rs b/exercises/11_hashmaps/hashmaps1.rs index 80829ea..5a52f61 100644 --- a/exercises/11_hashmaps/hashmaps1.rs +++ b/exercises/11_hashmaps/hashmaps1.rs @@ -11,8 +11,6 @@ // Execute `rustlings hint hashmaps1` or use the `hint` watch subcommand for a // hint. -// I AM NOT DONE - use std::collections::HashMap; fn fruit_basket() -> HashMap { diff --git a/exercises/11_hashmaps/hashmaps2.rs b/exercises/11_hashmaps/hashmaps2.rs index a592569..2730643 100644 --- a/exercises/11_hashmaps/hashmaps2.rs +++ b/exercises/11_hashmaps/hashmaps2.rs @@ -14,8 +14,6 @@ // Execute `rustlings hint hashmaps2` or use the `hint` watch subcommand for a // hint. -// I AM NOT DONE - use std::collections::HashMap; #[derive(Hash, PartialEq, Eq)] diff --git a/exercises/11_hashmaps/hashmaps3.rs b/exercises/11_hashmaps/hashmaps3.rs index 8d9236d..775a401 100644 --- a/exercises/11_hashmaps/hashmaps3.rs +++ b/exercises/11_hashmaps/hashmaps3.rs @@ -15,8 +15,6 @@ // Execute `rustlings hint hashmaps3` or use the `hint` watch subcommand for a // hint. -// I AM NOT DONE - use std::collections::HashMap; // A structure to store the goal details of a team. diff --git a/exercises/12_options/options1.rs b/exercises/12_options/options1.rs index 3cbfecd..ba4b1cd 100644 --- a/exercises/12_options/options1.rs +++ b/exercises/12_options/options1.rs @@ -3,8 +3,6 @@ // Execute `rustlings hint options1` or use the `hint` watch subcommand for a // hint. -// I AM NOT DONE - // This function returns how much icecream there is left in the fridge. // If it's before 10PM, there's 5 scoops left. At 10PM, someone eats it // all, so there'll be no more left :( diff --git a/exercises/12_options/options2.rs b/exercises/12_options/options2.rs index 4d998e7..73f707e 100644 --- a/exercises/12_options/options2.rs +++ b/exercises/12_options/options2.rs @@ -3,8 +3,6 @@ // Execute `rustlings hint options2` or use the `hint` watch subcommand for a // hint. -// I AM NOT DONE - #[cfg(test)] mod tests { #[test] diff --git a/exercises/12_options/options3.rs b/exercises/12_options/options3.rs index 23c15ea..7922ef9 100644 --- a/exercises/12_options/options3.rs +++ b/exercises/12_options/options3.rs @@ -3,8 +3,6 @@ // Execute `rustlings hint options3` or use the `hint` watch subcommand for a // hint. -// I AM NOT DONE - struct Point { x: i32, y: i32, diff --git a/exercises/13_error_handling/errors1.rs b/exercises/13_error_handling/errors1.rs index 0ba59a5..9767f2c 100644 --- a/exercises/13_error_handling/errors1.rs +++ b/exercises/13_error_handling/errors1.rs @@ -9,8 +9,6 @@ // Execute `rustlings hint errors1` or use the `hint` watch subcommand for a // hint. -// I AM NOT DONE - pub fn generate_nametag_text(name: String) -> Option { if name.is_empty() { // Empty names aren't allowed. diff --git a/exercises/13_error_handling/errors2.rs b/exercises/13_error_handling/errors2.rs index 631fe67..88d1bf4 100644 --- a/exercises/13_error_handling/errors2.rs +++ b/exercises/13_error_handling/errors2.rs @@ -19,8 +19,6 @@ // Execute `rustlings hint errors2` or use the `hint` watch subcommand for a // hint. -// I AM NOT DONE - use std::num::ParseIntError; pub fn total_cost(item_quantity: &str) -> Result { diff --git a/exercises/13_error_handling/errors3.rs b/exercises/13_error_handling/errors3.rs index d42d3b1..56bb31b 100644 --- a/exercises/13_error_handling/errors3.rs +++ b/exercises/13_error_handling/errors3.rs @@ -7,8 +7,6 @@ // Execute `rustlings hint errors3` or use the `hint` watch subcommand for a // hint. -// I AM NOT DONE - use std::num::ParseIntError; fn main() { diff --git a/exercises/13_error_handling/errors4.rs b/exercises/13_error_handling/errors4.rs index d6d6fcb..0e5c08b 100644 --- a/exercises/13_error_handling/errors4.rs +++ b/exercises/13_error_handling/errors4.rs @@ -3,8 +3,6 @@ // Execute `rustlings hint errors4` or use the `hint` watch subcommand for a // hint. -// I AM NOT DONE - #[derive(PartialEq, Debug)] struct PositiveNonzeroInteger(u64); diff --git a/exercises/13_error_handling/errors5.rs b/exercises/13_error_handling/errors5.rs index 92461a7..0bcb4b8 100644 --- a/exercises/13_error_handling/errors5.rs +++ b/exercises/13_error_handling/errors5.rs @@ -22,8 +22,6 @@ // Execute `rustlings hint errors5` or use the `hint` watch subcommand for a // hint. -// I AM NOT DONE - use std::error; use std::fmt; use std::num::ParseIntError; diff --git a/exercises/13_error_handling/errors6.rs b/exercises/13_error_handling/errors6.rs index aaf0948..de73a9a 100644 --- a/exercises/13_error_handling/errors6.rs +++ b/exercises/13_error_handling/errors6.rs @@ -9,8 +9,6 @@ // Execute `rustlings hint errors6` or use the `hint` watch subcommand for a // hint. -// I AM NOT DONE - use std::num::ParseIntError; // This is a custom error type that we will be using in `parse_pos_nonzero()`. diff --git a/exercises/14_generics/generics1.rs b/exercises/14_generics/generics1.rs index 35c1d2f..545fd95 100644 --- a/exercises/14_generics/generics1.rs +++ b/exercises/14_generics/generics1.rs @@ -6,8 +6,6 @@ // Execute `rustlings hint generics1` or use the `hint` watch subcommand for a // hint. -// I AM NOT DONE - fn main() { let mut shopping_list: Vec = Vec::new(); shopping_list.push("milk"); diff --git a/exercises/14_generics/generics2.rs b/exercises/14_generics/generics2.rs index 074cd93..d50ed17 100644 --- a/exercises/14_generics/generics2.rs +++ b/exercises/14_generics/generics2.rs @@ -6,8 +6,6 @@ // Execute `rustlings hint generics2` or use the `hint` watch subcommand for a // hint. -// I AM NOT DONE - struct Wrapper { value: u32, } diff --git a/exercises/15_traits/traits1.rs b/exercises/15_traits/traits1.rs index 37dfcbf..c51d3b8 100644 --- a/exercises/15_traits/traits1.rs +++ b/exercises/15_traits/traits1.rs @@ -7,8 +7,6 @@ // Execute `rustlings hint traits1` or use the `hint` watch subcommand for a // hint. -// I AM NOT DONE - trait AppendBar { fn append_bar(self) -> Self; } diff --git a/exercises/15_traits/traits2.rs b/exercises/15_traits/traits2.rs index 3e35f8e..9a2bc07 100644 --- a/exercises/15_traits/traits2.rs +++ b/exercises/15_traits/traits2.rs @@ -8,8 +8,6 @@ // // Execute `rustlings hint traits2` or use the `hint` watch subcommand for a hint. -// I AM NOT DONE - trait AppendBar { fn append_bar(self) -> Self; } diff --git a/exercises/15_traits/traits3.rs b/exercises/15_traits/traits3.rs index 4e2b06b..357f1d7 100644 --- a/exercises/15_traits/traits3.rs +++ b/exercises/15_traits/traits3.rs @@ -8,8 +8,6 @@ // Execute `rustlings hint traits3` or use the `hint` watch subcommand for a // hint. -// I AM NOT DONE - pub trait Licensed { fn licensing_info(&self) -> String; } diff --git a/exercises/15_traits/traits4.rs b/exercises/15_traits/traits4.rs index 4bda3e5..7242c48 100644 --- a/exercises/15_traits/traits4.rs +++ b/exercises/15_traits/traits4.rs @@ -7,8 +7,6 @@ // Execute `rustlings hint traits4` or use the `hint` watch subcommand for a // hint. -// I AM NOT DONE - pub trait Licensed { fn licensing_info(&self) -> String { "some information".to_string() diff --git a/exercises/15_traits/traits5.rs b/exercises/15_traits/traits5.rs index df18380..f258d32 100644 --- a/exercises/15_traits/traits5.rs +++ b/exercises/15_traits/traits5.rs @@ -7,8 +7,6 @@ // Execute `rustlings hint traits5` or use the `hint` watch subcommand for a // hint. -// I AM NOT DONE - pub trait SomeTrait { fn some_function(&self) -> bool { true diff --git a/exercises/16_lifetimes/lifetimes1.rs b/exercises/16_lifetimes/lifetimes1.rs index 87bde49..4f544b4 100644 --- a/exercises/16_lifetimes/lifetimes1.rs +++ b/exercises/16_lifetimes/lifetimes1.rs @@ -8,8 +8,6 @@ // Execute `rustlings hint lifetimes1` or use the `hint` watch subcommand for a // hint. -// I AM NOT DONE - fn longest(x: &str, y: &str) -> &str { if x.len() > y.len() { x diff --git a/exercises/16_lifetimes/lifetimes2.rs b/exercises/16_lifetimes/lifetimes2.rs index 4f3d8c1..33b5565 100644 --- a/exercises/16_lifetimes/lifetimes2.rs +++ b/exercises/16_lifetimes/lifetimes2.rs @@ -6,8 +6,6 @@ // Execute `rustlings hint lifetimes2` or use the `hint` watch subcommand for a // hint. -// I AM NOT DONE - fn longest<'a>(x: &'a str, y: &'a str) -> &'a str { if x.len() > y.len() { x diff --git a/exercises/16_lifetimes/lifetimes3.rs b/exercises/16_lifetimes/lifetimes3.rs index 9c59f9c..de6005e 100644 --- a/exercises/16_lifetimes/lifetimes3.rs +++ b/exercises/16_lifetimes/lifetimes3.rs @@ -5,8 +5,6 @@ // Execute `rustlings hint lifetimes3` or use the `hint` watch subcommand for a // hint. -// I AM NOT DONE - struct Book { author: &str, title: &str, diff --git a/exercises/17_tests/tests1.rs b/exercises/17_tests/tests1.rs index 810277a..bde2108 100644 --- a/exercises/17_tests/tests1.rs +++ b/exercises/17_tests/tests1.rs @@ -10,8 +10,6 @@ // Execute `rustlings hint tests1` or use the `hint` watch subcommand for a // hint. -// I AM NOT DONE - #[cfg(test)] mod tests { #[test] diff --git a/exercises/17_tests/tests2.rs b/exercises/17_tests/tests2.rs index f8024e9..aea5c0e 100644 --- a/exercises/17_tests/tests2.rs +++ b/exercises/17_tests/tests2.rs @@ -6,8 +6,6 @@ // Execute `rustlings hint tests2` or use the `hint` watch subcommand for a // hint. -// I AM NOT DONE - #[cfg(test)] mod tests { #[test] diff --git a/exercises/17_tests/tests3.rs b/exercises/17_tests/tests3.rs index 4013e38..d815e05 100644 --- a/exercises/17_tests/tests3.rs +++ b/exercises/17_tests/tests3.rs @@ -7,8 +7,6 @@ // Execute `rustlings hint tests3` or use the `hint` watch subcommand for a // hint. -// I AM NOT DONE - pub fn is_even(num: i32) -> bool { num % 2 == 0 } diff --git a/exercises/17_tests/tests4.rs b/exercises/17_tests/tests4.rs index 935d0db..0972a5b 100644 --- a/exercises/17_tests/tests4.rs +++ b/exercises/17_tests/tests4.rs @@ -5,8 +5,6 @@ // Execute `rustlings hint tests4` or use the `hint` watch subcommand for a // hint. -// I AM NOT DONE - struct Rectangle { width: i32, height: i32 diff --git a/exercises/18_iterators/iterators1.rs b/exercises/18_iterators/iterators1.rs index 31076bb..7ec7da2 100644 --- a/exercises/18_iterators/iterators1.rs +++ b/exercises/18_iterators/iterators1.rs @@ -9,8 +9,6 @@ // Execute `rustlings hint iterators1` or use the `hint` watch subcommand for a // hint. -// I AM NOT DONE - #[test] fn main() { let my_fav_fruits = vec!["banana", "custard apple", "avocado", "peach", "raspberry"]; diff --git a/exercises/18_iterators/iterators2.rs b/exercises/18_iterators/iterators2.rs index dda82a0..4ca7742 100644 --- a/exercises/18_iterators/iterators2.rs +++ b/exercises/18_iterators/iterators2.rs @@ -6,8 +6,6 @@ // Execute `rustlings hint iterators2` or use the `hint` watch subcommand for a // hint. -// I AM NOT DONE - // Step 1. // Complete the `capitalize_first` function. // "hello" -> "Hello" diff --git a/exercises/18_iterators/iterators3.rs b/exercises/18_iterators/iterators3.rs index 29fa23a..f7da049 100644 --- a/exercises/18_iterators/iterators3.rs +++ b/exercises/18_iterators/iterators3.rs @@ -9,8 +9,6 @@ // Execute `rustlings hint iterators3` or use the `hint` watch subcommand for a // hint. -// I AM NOT DONE - #[derive(Debug, PartialEq, Eq)] pub enum DivisionError { NotDivisible(NotDivisibleError), diff --git a/exercises/18_iterators/iterators4.rs b/exercises/18_iterators/iterators4.rs index 3c0724e..af3958c 100644 --- a/exercises/18_iterators/iterators4.rs +++ b/exercises/18_iterators/iterators4.rs @@ -3,8 +3,6 @@ // Execute `rustlings hint iterators4` or use the `hint` watch subcommand for a // hint. -// I AM NOT DONE - pub fn factorial(num: u64) -> u64 { // Complete this function to return the factorial of num // Do not use: diff --git a/exercises/18_iterators/iterators5.rs b/exercises/18_iterators/iterators5.rs index a062ee4..ceec536 100644 --- a/exercises/18_iterators/iterators5.rs +++ b/exercises/18_iterators/iterators5.rs @@ -11,8 +11,6 @@ // Execute `rustlings hint iterators5` or use the `hint` watch subcommand for a // hint. -// I AM NOT DONE - use std::collections::HashMap; #[derive(Clone, Copy, PartialEq, Eq)] diff --git a/exercises/19_smart_pointers/arc1.rs b/exercises/19_smart_pointers/arc1.rs index 3526ddc..0647eea 100644 --- a/exercises/19_smart_pointers/arc1.rs +++ b/exercises/19_smart_pointers/arc1.rs @@ -21,8 +21,6 @@ // // Execute `rustlings hint arc1` or use the `hint` watch subcommand for a hint. -// I AM NOT DONE - #![forbid(unused_imports)] // Do not change this, (or the next) line. use std::sync::Arc; use std::thread; diff --git a/exercises/19_smart_pointers/box1.rs b/exercises/19_smart_pointers/box1.rs index 513e7da..2abc024 100644 --- a/exercises/19_smart_pointers/box1.rs +++ b/exercises/19_smart_pointers/box1.rs @@ -18,8 +18,6 @@ // // Execute `rustlings hint box1` or use the `hint` watch subcommand for a hint. -// I AM NOT DONE - #[derive(PartialEq, Debug)] pub enum List { Cons(i32, List), diff --git a/exercises/19_smart_pointers/cow1.rs b/exercises/19_smart_pointers/cow1.rs index fcd3e0b..b24591b 100644 --- a/exercises/19_smart_pointers/cow1.rs +++ b/exercises/19_smart_pointers/cow1.rs @@ -12,8 +12,6 @@ // // Execute `rustlings hint cow1` or use the `hint` watch subcommand for a hint. -// I AM NOT DONE - use std::borrow::Cow; fn abs_all<'a, 'b>(input: &'a mut Cow<'b, [i32]>) -> &'a mut Cow<'b, [i32]> { diff --git a/exercises/19_smart_pointers/rc1.rs b/exercises/19_smart_pointers/rc1.rs index 1b90346..e96e625 100644 --- a/exercises/19_smart_pointers/rc1.rs +++ b/exercises/19_smart_pointers/rc1.rs @@ -10,8 +10,6 @@ // // Execute `rustlings hint rc1` or use the `hint` watch subcommand for a hint. -// I AM NOT DONE - use std::rc::Rc; #[derive(Debug)] diff --git a/exercises/20_threads/threads1.rs b/exercises/20_threads/threads1.rs index 80b6def..be1301d 100644 --- a/exercises/20_threads/threads1.rs +++ b/exercises/20_threads/threads1.rs @@ -8,8 +8,6 @@ // Execute `rustlings hint threads1` or use the `hint` watch subcommand for a // hint. -// I AM NOT DONE - use std::thread; use std::time::{Duration, Instant}; diff --git a/exercises/20_threads/threads2.rs b/exercises/20_threads/threads2.rs index 60d6824..13cb840 100644 --- a/exercises/20_threads/threads2.rs +++ b/exercises/20_threads/threads2.rs @@ -7,8 +7,6 @@ // Execute `rustlings hint threads2` or use the `hint` watch subcommand for a // hint. -// I AM NOT DONE - use std::sync::Arc; use std::thread; use std::time::Duration; diff --git a/exercises/20_threads/threads3.rs b/exercises/20_threads/threads3.rs index acb97b4..35b914a 100644 --- a/exercises/20_threads/threads3.rs +++ b/exercises/20_threads/threads3.rs @@ -3,8 +3,6 @@ // Execute `rustlings hint threads3` or use the `hint` watch subcommand for a // hint. -// I AM NOT DONE - use std::sync::mpsc; use std::sync::Arc; use std::thread; diff --git a/exercises/21_macros/macros1.rs b/exercises/21_macros/macros1.rs index 678de6e..65986db 100644 --- a/exercises/21_macros/macros1.rs +++ b/exercises/21_macros/macros1.rs @@ -3,8 +3,6 @@ // Execute `rustlings hint macros1` or use the `hint` watch subcommand for a // hint. -// I AM NOT DONE - macro_rules! my_macro { () => { println!("Check out my macro!"); diff --git a/exercises/21_macros/macros2.rs b/exercises/21_macros/macros2.rs index 788fc16..b7c37fd 100644 --- a/exercises/21_macros/macros2.rs +++ b/exercises/21_macros/macros2.rs @@ -3,8 +3,6 @@ // Execute `rustlings hint macros2` or use the `hint` watch subcommand for a // hint. -// I AM NOT DONE - fn main() { my_macro!(); } diff --git a/exercises/21_macros/macros3.rs b/exercises/21_macros/macros3.rs index b795c14..92a1922 100644 --- a/exercises/21_macros/macros3.rs +++ b/exercises/21_macros/macros3.rs @@ -5,8 +5,6 @@ // Execute `rustlings hint macros3` or use the `hint` watch subcommand for a // hint. -// I AM NOT DONE - mod macros { macro_rules! my_macro { () => { diff --git a/exercises/21_macros/macros4.rs b/exercises/21_macros/macros4.rs index 71b45a0..83a6e44 100644 --- a/exercises/21_macros/macros4.rs +++ b/exercises/21_macros/macros4.rs @@ -3,8 +3,6 @@ // Execute `rustlings hint macros4` or use the `hint` watch subcommand for a // hint. -// I AM NOT DONE - #[rustfmt::skip] macro_rules! my_macro { () => { diff --git a/exercises/22_clippy/clippy1.rs b/exercises/22_clippy/clippy1.rs index e0c6ce7c4..1e0f42e 100644 --- a/exercises/22_clippy/clippy1.rs +++ b/exercises/22_clippy/clippy1.rs @@ -9,8 +9,6 @@ // Execute `rustlings hint clippy1` or use the `hint` watch subcommand for a // hint. -// I AM NOT DONE - use std::f32; fn main() { diff --git a/exercises/22_clippy/clippy2.rs b/exercises/22_clippy/clippy2.rs index 9b87a0b..37ac089 100644 --- a/exercises/22_clippy/clippy2.rs +++ b/exercises/22_clippy/clippy2.rs @@ -3,8 +3,6 @@ // Execute `rustlings hint clippy2` or use the `hint` watch subcommand for a // hint. -// I AM NOT DONE - fn main() { let mut res = 42; let option = Some(12); diff --git a/exercises/22_clippy/clippy3.rs b/exercises/22_clippy/clippy3.rs index 5a95f5b..6a6a36b 100644 --- a/exercises/22_clippy/clippy3.rs +++ b/exercises/22_clippy/clippy3.rs @@ -3,8 +3,6 @@ // Here's a couple more easy Clippy fixes, so you can see its utility. // No hints. -// I AM NOT DONE - #[allow(unused_variables, unused_assignments)] fn main() { let my_option: Option<()> = None; diff --git a/exercises/23_conversions/as_ref_mut.rs b/exercises/23_conversions/as_ref_mut.rs index 2ba9e3f..cd2c93b 100644 --- a/exercises/23_conversions/as_ref_mut.rs +++ b/exercises/23_conversions/as_ref_mut.rs @@ -7,8 +7,6 @@ // Execute `rustlings hint as_ref_mut` or use the `hint` watch subcommand for a // hint. -// I AM NOT DONE - // Obtain the number of bytes (not characters) in the given argument. // TODO: Add the AsRef trait appropriately as a trait bound. fn byte_counter(arg: T) -> usize { diff --git a/exercises/23_conversions/from_into.rs b/exercises/23_conversions/from_into.rs index 11787c3..d2a1609 100644 --- a/exercises/23_conversions/from_into.rs +++ b/exercises/23_conversions/from_into.rs @@ -41,8 +41,6 @@ impl Default for Person { // If while parsing the age, something goes wrong, then return the default of // Person Otherwise, then return an instantiated Person object with the results -// I AM NOT DONE - impl From<&str> for Person { fn from(s: &str) -> Person {} } diff --git a/exercises/23_conversions/from_str.rs b/exercises/23_conversions/from_str.rs index e209347..ed91ca5 100644 --- a/exercises/23_conversions/from_str.rs +++ b/exercises/23_conversions/from_str.rs @@ -31,8 +31,6 @@ enum ParsePersonError { ParseInt(ParseIntError), } -// I AM NOT DONE - // Steps: // 1. If the length of the provided string is 0, an error should be returned // 2. Split the given string on the commas present in it diff --git a/exercises/23_conversions/try_from_into.rs b/exercises/23_conversions/try_from_into.rs index 32d6ef3..2316655 100644 --- a/exercises/23_conversions/try_from_into.rs +++ b/exercises/23_conversions/try_from_into.rs @@ -27,8 +27,6 @@ enum IntoColorError { IntConversion, } -// I AM NOT DONE - // Your task is to complete this implementation and return an Ok result of inner // type Color. You need to create an implementation for a tuple of three // integers, an array of three integers, and a slice of integers. diff --git a/exercises/23_conversions/using_as.rs b/exercises/23_conversions/using_as.rs index 414cef3..9f617ec 100644 --- a/exercises/23_conversions/using_as.rs +++ b/exercises/23_conversions/using_as.rs @@ -10,8 +10,6 @@ // Execute `rustlings hint using_as` or use the `hint` watch subcommand for a // hint. -// I AM NOT DONE - fn average(values: &[f64]) -> f64 { let total = values.iter().sum::(); total / values.len() diff --git a/exercises/quiz1.rs b/exercises/quiz1.rs index 4ee5ada..b9e71f5 100644 --- a/exercises/quiz1.rs +++ b/exercises/quiz1.rs @@ -13,8 +13,6 @@ // // No hints this time ;) -// I AM NOT DONE - // Put your function here! // fn calculate_price_of_apples { diff --git a/exercises/quiz2.rs b/exercises/quiz2.rs index 29925ca..8ace3fe 100644 --- a/exercises/quiz2.rs +++ b/exercises/quiz2.rs @@ -20,8 +20,6 @@ // // No hints this time! -// I AM NOT DONE - pub enum Command { Uppercase, Trim, diff --git a/exercises/quiz3.rs b/exercises/quiz3.rs index 3b01d31..24f7082 100644 --- a/exercises/quiz3.rs +++ b/exercises/quiz3.rs @@ -16,8 +16,6 @@ // // Execute `rustlings hint quiz3` or use the `hint` watch subcommand for a hint. -// I AM NOT DONE - pub struct ReportCard { pub grade: f32, pub student_name: String, diff --git a/info.toml b/info.toml index 36629b3..c085e89 100644 --- a/info.toml +++ b/info.toml @@ -4,6 +4,7 @@ name = "intro1" path = "exercises/00_intro/intro1.rs" mode = "compile" +# TODO: Fix hint hint = """ Remove the `I AM NOT DONE` comment in the `exercises/intro00/intro1.rs` file to move on to the next exercise.""" @@ -129,11 +130,7 @@ path = "exercises/02_functions/functions3.rs" mode = "compile" hint = """ This time, the function *declaration* is okay, but there's something wrong -with the place where we're calling the function. - -As a reminder, you can freely play around with different solutions in Rustlings! -Watch mode will only jump to the next exercise if you remove the `I AM NOT -DONE` comment.""" +with the place where we're calling the function.""" [[exercises]] name = "functions4" diff --git a/src/app_state.rs b/src/app_state.rs new file mode 100644 index 0000000..4a0912e --- /dev/null +++ b/src/app_state.rs @@ -0,0 +1,185 @@ +use anyhow::{bail, Context, Result}; +use serde::{Deserialize, Serialize}; +use std::fs; + +use crate::exercise::Exercise; + +const BAD_INDEX_ERR: &str = "The current exercise index is higher than the number of exercises"; + +#[derive(Serialize, Deserialize)] +#[serde(deny_unknown_fields)] +struct StateFile { + current_exercise_ind: usize, + progress: Vec, +} + +impl StateFile { + fn read(exercises: &[Exercise]) -> Option { + let file_content = fs::read(".rustlings-state.json").ok()?; + + let slf: Self = serde_json::de::from_slice(&file_content).ok()?; + + if slf.progress.len() != exercises.len() || slf.current_exercise_ind >= exercises.len() { + return None; + } + + Some(slf) + } + + fn read_or_default(exercises: &[Exercise]) -> Self { + Self::read(exercises).unwrap_or_else(|| Self { + current_exercise_ind: 0, + progress: vec![false; exercises.len()], + }) + } + + fn write(&self) -> Result<()> { + let mut buf = Vec::with_capacity(1024); + serde_json::ser::to_writer(&mut buf, self).context("Failed to serialize the state")?; + fs::write(".rustlings-state.json", buf) + .context("Failed to write the state file `.rustlings-state.json`")?; + + Ok(()) + } +} + +pub struct AppState { + state_file: StateFile, + exercises: &'static [Exercise], + n_done: u16, + current_exercise: &'static Exercise, +} + +#[must_use] +pub enum ExercisesProgress { + AllDone, + Pending, +} + +impl AppState { + pub fn new(exercises: Vec) -> Self { + // Leaking for sending the exercises to the debounce event handler. + // Leaking is not a problem since the exercises' slice is used until the end of the program. + let exercises = exercises.leak(); + + let state_file = StateFile::read_or_default(exercises); + let n_done = state_file + .progress + .iter() + .fold(0, |acc, done| acc + u16::from(*done)); + let current_exercise = &exercises[state_file.current_exercise_ind]; + + Self { + state_file, + exercises, + n_done, + current_exercise, + } + } + + #[inline] + pub fn current_exercise_ind(&self) -> usize { + self.state_file.current_exercise_ind + } + + #[inline] + pub fn progress(&self) -> &[bool] { + &self.state_file.progress + } + + #[inline] + pub fn exercises(&self) -> &'static [Exercise] { + self.exercises + } + + #[inline] + pub fn n_done(&self) -> u16 { + self.n_done + } + + #[inline] + pub fn current_exercise(&self) -> &'static Exercise { + self.current_exercise + } + + pub fn set_current_exercise_ind(&mut self, ind: usize) -> Result<()> { + if ind >= self.exercises.len() { + bail!(BAD_INDEX_ERR); + } + + self.state_file.current_exercise_ind = ind; + self.current_exercise = &self.exercises[ind]; + + self.state_file.write() + } + + pub fn set_current_exercise_by_name(&mut self, name: &str) -> Result<()> { + let (ind, exercise) = self + .exercises + .iter() + .enumerate() + .find(|(_, exercise)| exercise.name == name) + .with_context(|| format!("No exercise found for '{name}'!"))?; + + self.state_file.current_exercise_ind = ind; + self.current_exercise = exercise; + + self.state_file.write() + } + + pub fn set_pending(&mut self, ind: usize) -> Result<()> { + let done = self + .state_file + .progress + .get_mut(ind) + .context(BAD_INDEX_ERR)?; + + if *done { + *done = false; + self.n_done -= 1; + self.state_file.write()?; + } + + Ok(()) + } + + fn next_exercise_ind(&self) -> Option { + let current_ind = self.state_file.current_exercise_ind; + + if current_ind == self.state_file.progress.len() - 1 { + // The last exercise is done. + // Search for exercises not done from the start. + return self.state_file.progress[..current_ind] + .iter() + .position(|done| !done); + } + + // The done exercise isn't the last one. + // Search for a pending exercise after the current one and then from the start. + match self.state_file.progress[current_ind + 1..] + .iter() + .position(|done| !done) + { + Some(ind) => Some(current_ind + 1 + ind), + None => self.state_file.progress[..current_ind] + .iter() + .position(|done| !done), + } + } + + pub fn done_current_exercise(&mut self) -> Result { + let done = &mut self.state_file.progress[self.state_file.current_exercise_ind]; + if !*done { + *done = true; + self.n_done += 1; + } + + let Some(ind) = self.next_exercise_ind() else { + return Ok(ExercisesProgress::AllDone); + }; + + self.set_current_exercise_ind(ind)?; + + Ok(ExercisesProgress::Pending) + } +} diff --git a/src/exercise.rs b/src/exercise.rs index ca47009..de435d1 100644 --- a/src/exercise.rs +++ b/src/exercise.rs @@ -1,38 +1,14 @@ use anyhow::{Context, Result}; use serde::Deserialize; use std::{ - array, fmt::{self, Debug, Display, Formatter}, - fs::{self, File}, - io::{self, BufRead, BufReader}, - mem, + fs::{self}, path::PathBuf, process::{Command, Output}, }; -use winnow::{ - ascii::{space0, Caseless}, - combinator::opt, - Parser, -}; use crate::embedded::{WriteStrategy, EMBEDDED_FILES}; -// The number of context lines above and below a highlighted line. -const CONTEXT: usize = 2; - -// Check if the line contains the "I AM NOT DONE" comment. -fn contains_not_done_comment(input: &str) -> bool { - ( - space0::<_, ()>, - "//", - opt('/'), - space0, - Caseless("I AM NOT DONE"), - ) - .parse_next(&mut &*input) - .is_ok() -} - // The mode of the exercise. #[derive(Deserialize, Copy, Clone)] #[serde(rename_all = "lowercase")] @@ -78,13 +54,6 @@ pub struct Exercise { pub hint: String, } -// The state of an Exercise. -#[derive(PartialEq, Eq, Debug)] -pub enum State { - Done, - Pending(Vec), -} - // The context information of a pending exercise. #[derive(PartialEq, Eq, Debug)] pub struct ContextLine { @@ -129,105 +98,6 @@ impl Exercise { } } - pub fn state(&self) -> Result { - let source_file = File::open(&self.path) - .with_context(|| format!("Failed to open the exercise file {}", self.path.display()))?; - let mut source_reader = BufReader::new(source_file); - - // Read the next line into `buf` without the newline at the end. - let mut read_line = |buf: &mut String| -> io::Result<_> { - let n = source_reader.read_line(buf)?; - if buf.ends_with('\n') { - buf.pop(); - if buf.ends_with('\r') { - buf.pop(); - } - } - Ok(n) - }; - - let mut current_line_number: usize = 1; - // Keep the last `CONTEXT` lines while iterating over the file lines. - let mut prev_lines: [_; CONTEXT] = array::from_fn(|_| String::with_capacity(256)); - let mut line = String::with_capacity(256); - - loop { - let n = read_line(&mut line).with_context(|| { - format!("Failed to read the exercise file {}", self.path.display()) - })?; - - // Reached the end of the file and didn't find the comment. - if n == 0 { - return Ok(State::Done); - } - - if contains_not_done_comment(&line) { - let mut context = Vec::with_capacity(2 * CONTEXT + 1); - // Previous lines. - for (ind, prev_line) in prev_lines - .into_iter() - .take(current_line_number - 1) - .enumerate() - .rev() - { - context.push(ContextLine { - line: prev_line, - number: current_line_number - 1 - ind, - important: false, - }); - } - - // Current line. - context.push(ContextLine { - line, - number: current_line_number, - important: true, - }); - - // Next lines. - for ind in 0..CONTEXT { - let mut next_line = String::with_capacity(256); - let Ok(n) = read_line(&mut next_line) else { - // If an error occurs, just ignore the next lines. - break; - }; - - // Reached the end of the file. - if n == 0 { - break; - } - - context.push(ContextLine { - line: next_line, - number: current_line_number + 1 + ind, - important: false, - }); - } - - return Ok(State::Pending(context)); - } - - current_line_number += 1; - // Add the current line as a previous line and shift the older lines by one. - for prev_line in &mut prev_lines { - mem::swap(&mut line, prev_line); - } - // The current line now contains the oldest previous line. - // Recycle it for reading the next line. - line.clear(); - } - } - - // Check that the exercise looks to be solved using self.state() - // This is not the best way to check since - // the user can just remove the "I AM NOT DONE" string from the file - // without actually having solved anything. - // The only other way to truly check this would to compile and run - // the exercise; which would be both costly and counterintuitive - pub fn looks_done(&self) -> Result { - self.state().map(|state| state == State::Done) - } - pub fn reset(&self) -> Result<()> { EMBEDDED_FILES .write_exercise_to_disk(&self.path, WriteStrategy::Overwrite) @@ -240,77 +110,3 @@ impl Display for Exercise { self.path.fmt(f) } } - -#[cfg(test)] -mod test { - use super::*; - - #[test] - fn test_pending_state() { - let exercise = Exercise { - name: "pending_exercise".into(), - path: PathBuf::from("tests/fixture/state/exercises/pending_exercise.rs"), - mode: Mode::Compile, - hint: String::new(), - }; - - let state = exercise.state(); - let expected = vec![ - ContextLine { - line: "// fake_exercise".to_string(), - number: 1, - important: false, - }, - ContextLine { - line: "".to_string(), - number: 2, - important: false, - }, - ContextLine { - line: "// I AM NOT DONE".to_string(), - number: 3, - important: true, - }, - ContextLine { - line: "".to_string(), - number: 4, - important: false, - }, - ContextLine { - line: "fn main() {".to_string(), - number: 5, - important: false, - }, - ]; - - assert_eq!(state.unwrap(), State::Pending(expected)); - } - - #[test] - fn test_finished_exercise() { - let exercise = Exercise { - name: "finished_exercise".into(), - path: PathBuf::from("tests/fixture/state/exercises/finished_exercise.rs"), - mode: Mode::Compile, - hint: String::new(), - }; - - assert_eq!(exercise.state().unwrap(), State::Done); - } - - #[test] - fn test_not_done() { - assert!(contains_not_done_comment("// I AM NOT DONE")); - assert!(contains_not_done_comment("/// I AM NOT DONE")); - assert!(contains_not_done_comment("// I AM NOT DONE")); - assert!(contains_not_done_comment("/// I AM NOT DONE")); - assert!(contains_not_done_comment("// I AM NOT DONE ")); - assert!(contains_not_done_comment("// I AM NOT DONE!")); - assert!(contains_not_done_comment("// I am not done")); - assert!(contains_not_done_comment("// i am NOT done")); - - assert!(!contains_not_done_comment("I AM NOT DONE")); - assert!(!contains_not_done_comment("// NOT DONE")); - assert!(!contains_not_done_comment("DONE")); - } -} diff --git a/src/list.rs b/src/list.rs index 560b85a..80b78e8 100644 --- a/src/list.rs +++ b/src/list.rs @@ -9,11 +9,11 @@ use std::{fmt::Write, io}; mod state; -use crate::{exercise::Exercise, state_file::StateFile}; +use crate::app_state::AppState; use self::state::{Filter, UiState}; -pub fn list(state_file: &mut StateFile, exercises: &'static [Exercise]) -> Result<()> { +pub fn list(app_state: &mut AppState) -> Result<()> { let mut stdout = io::stdout().lock(); stdout.execute(EnterAlternateScreen)?; enable_raw_mode()?; @@ -21,7 +21,7 @@ pub fn list(state_file: &mut StateFile, exercises: &'static [Exercise]) -> Resul let mut terminal = Terminal::new(CrosstermBackend::new(&mut stdout))?; terminal.clear()?; - let mut ui_state = UiState::new(state_file, exercises); + let mut ui_state = UiState::new(app_state); 'outer: loop { terminal.draw(|frame| ui_state.draw(frame).unwrap())?; @@ -56,7 +56,7 @@ pub fn list(state_file: &mut StateFile, exercises: &'static [Exercise]) -> Resul "Enabled filter DONE │ Press d again to disable the filter" }; - ui_state = ui_state.with_updated_rows(state_file); + ui_state = ui_state.with_updated_rows(); ui_state.message.push_str(message); } KeyCode::Char('p') => { @@ -68,23 +68,20 @@ pub fn list(state_file: &mut StateFile, exercises: &'static [Exercise]) -> Resul "Enabled filter PENDING │ Press p again to disable the filter" }; - ui_state = ui_state.with_updated_rows(state_file); + ui_state = ui_state.with_updated_rows(); ui_state.message.push_str(message); } KeyCode::Char('r') => { - let selected = ui_state.selected(); - let exercise = &exercises[selected]; - exercise.reset()?; - state_file.reset(selected)?; + let exercise = ui_state.reset_selected()?; - ui_state = ui_state.with_updated_rows(state_file); + ui_state = ui_state.with_updated_rows(); ui_state .message .write_fmt(format_args!("The exercise {exercise} has been reset!"))?; } KeyCode::Char('c') => { - state_file.set_next_exercise_ind(ui_state.selected())?; - ui_state = ui_state.with_updated_rows(state_file); + ui_state.selected_to_current_exercise()?; + ui_state = ui_state.with_updated_rows(); } _ => (), } diff --git a/src/list/state.rs b/src/list/state.rs index 209374b..7714268 100644 --- a/src/list/state.rs +++ b/src/list/state.rs @@ -7,7 +7,7 @@ use ratatui::{ Frame, }; -use crate::{exercise::Exercise, progress_bar::progress_bar_ratatui, state_file::StateFile}; +use crate::{app_state::AppState, exercise::Exercise, progress_bar::progress_bar_ratatui}; #[derive(Copy, Clone, PartialEq, Eq)] pub enum Filter { @@ -16,30 +16,29 @@ pub enum Filter { None, } -pub struct UiState { +pub struct UiState<'a> { pub table: Table<'static>, pub message: String, pub filter: Filter, - exercises: &'static [Exercise], - progress: u16, - selected: usize, + app_state: &'a mut AppState, table_state: TableState, + selected: usize, last_ind: usize, } -impl UiState { - pub fn with_updated_rows(mut self, state_file: &StateFile) -> Self { +impl<'a> UiState<'a> { + pub fn with_updated_rows(mut self) -> Self { + let current_exercise_ind = self.app_state.current_exercise_ind(); + let mut rows_counter: usize = 0; - let mut progress: u16 = 0; let rows = self - .exercises + .app_state + .exercises() .iter() - .zip(state_file.progress().iter().copied()) + .zip(self.app_state.progress().iter().copied()) .enumerate() .filter_map(|(ind, (exercise, done))| { let exercise_state = if done { - progress += 1; - if self.filter == Filter::Pending { return None; } @@ -55,7 +54,7 @@ impl UiState { rows_counter += 1; - let next = if ind == state_file.next_exercise_ind() { + let next = if ind == current_exercise_ind { ">>>>".bold().red() } else { Span::default() @@ -74,15 +73,14 @@ impl UiState { self.last_ind = rows_counter.saturating_sub(1); self.select(self.selected.min(self.last_ind)); - self.progress = progress; - self } - pub fn new(state_file: &StateFile, exercises: &'static [Exercise]) -> Self { + pub fn new(app_state: &'a mut AppState) -> Self { let header = Row::new(["Next", "State", "Name", "Path"]); - let max_name_len = exercises + let max_name_len = app_state + .exercises() .iter() .map(|exercise| exercise.name.len()) .max() @@ -104,7 +102,7 @@ impl UiState { .highlight_symbol("🦀") .block(Block::default().borders(Borders::BOTTOM)); - let selected = state_file.next_exercise_ind(); + let selected = app_state.current_exercise_ind(); let table_state = TableState::default() .with_offset(selected.saturating_sub(10)) .with_selected(Some(selected)); @@ -113,19 +111,13 @@ impl UiState { table, message: String::with_capacity(128), filter: Filter::None, - exercises, - progress: 0, - selected, + app_state, table_state, + selected, last_ind: 0, }; - slf.with_updated_rows(state_file) - } - - #[inline] - pub fn selected(&self) -> usize { - self.selected + slf.with_updated_rows() } fn select(&mut self, ind: usize) { @@ -134,11 +126,13 @@ impl UiState { } pub fn select_next(&mut self) { - self.select(self.selected.saturating_add(1).min(self.last_ind)); + let next = (self.selected + 1).min(self.last_ind); + self.select(next); } pub fn select_previous(&mut self) { - self.select(self.selected.saturating_sub(1)); + let previous = self.selected.saturating_sub(1); + self.select(previous); } #[inline] @@ -167,8 +161,8 @@ impl UiState { frame.render_widget( Paragraph::new(progress_bar_ratatui( - self.progress, - self.exercises.len() as u16, + self.app_state.n_done(), + self.app_state.exercises().len() as u16, area.width, )?) .block(Block::default().borders(Borders::BOTTOM)), @@ -200,4 +194,19 @@ impl UiState { Ok(()) } + + pub fn reset_selected(&mut self) -> Result<&'static Exercise> { + self.app_state.set_pending(self.selected)?; + // TODO: Take care of filters! + let exercise = &self.app_state.exercises()[self.selected]; + exercise.reset()?; + + Ok(exercise) + } + + #[inline] + pub fn selected_to_current_exercise(&mut self) -> Result<()> { + // TODO: Take care of filters! + self.app_state.set_current_exercise_ind(self.selected) + } } diff --git a/src/main.rs b/src/main.rs index fc83e0f..926605c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,7 +1,8 @@ -use anyhow::{bail, Context, Result}; +use anyhow::{Context, Result}; use clap::{Parser, Subcommand}; use std::{path::Path, process::exit}; +mod app_state; mod consts; mod embedded; mod exercise; @@ -9,17 +10,15 @@ mod init; mod list; mod progress_bar; mod run; -mod state_file; -mod verify; mod watch; use self::{ + app_state::AppState, consts::WELCOME, - exercise::{Exercise, InfoFile}, + exercise::InfoFile, + init::init, list::list, run::run, - state_file::StateFile, - verify::{verify, VerifyState}, watch::{watch, WatchExit}, }; @@ -35,14 +34,12 @@ struct Args { enum Subcommands { /// Initialize Rustlings Init, - /// Verify all exercises according to the recommended order - Verify, /// Same as just running `rustlings` without a subcommand. Watch, - /// Run/Test a single exercise + /// Run a single exercise. Runs the next pending exercise if the exercise name is not specified. Run { /// The name of the exercise - name: String, + name: Option, }, /// Reset a single exercise Reset { @@ -56,26 +53,6 @@ enum Subcommands { }, } -fn find_exercise(name: &str, exercises: &'static [Exercise]) -> Result<(usize, &'static Exercise)> { - if name == "next" { - for (ind, exercise) in exercises.iter().enumerate() { - if !exercise.looks_done()? { - return Ok((ind, exercise)); - } - } - - println!("🎉 Congratulations! You have done all the exercises!"); - println!("🔚 There are no more exercises to do next!"); - exit(0); - } - - exercises - .iter() - .enumerate() - .find(|(_, exercise)| exercise.name == name) - .with_context(|| format!("No exercise found for '{name}'!")) -} - fn main() -> Result<()> { let args = Args::parse(); @@ -87,11 +64,10 @@ Try running `cargo --version` to diagnose the problem.", let mut info_file = InfoFile::parse()?; info_file.exercises.shrink_to_fit(); - // Leaking is not a problem since the exercises' slice is used until the end of the program. - let exercises = info_file.exercises.leak(); + let exercises = info_file.exercises; if matches!(args.command, Some(Subcommands::Init)) { - init::init(exercises).context("Initialization failed")?; + init(&exercises).context("Initialization failed")?; println!( "\nDone initialization!\n Run `cd rustlings` to go into the generated directory. @@ -109,38 +85,37 @@ If you are just starting with Rustlings, run the command `rustlings init` to ini exit(1); } - let mut state_file = StateFile::read_or_default(exercises); + let mut app_state = AppState::new(exercises); match args.command { None | Some(Subcommands::Watch) => loop { - match watch(&mut state_file, exercises)? { + match watch(&mut app_state)? { WatchExit::Shutdown => break, // It is much easier to exit the watch mode, launch the list mode and then restart // the watch mode instead of trying to pause the watch threads and correct the // watch state. - WatchExit::List => list(&mut state_file, exercises)?, + WatchExit::List => list(&mut app_state)?, } }, // `Init` is handled above. Some(Subcommands::Init) => (), Some(Subcommands::Run { name }) => { - let (_, exercise) = find_exercise(&name, exercises)?; - run(exercise).unwrap_or_else(|_| exit(1)); + if let Some(name) = name { + app_state.set_current_exercise_by_name(&name)?; + } + run(&mut app_state)?; } Some(Subcommands::Reset { name }) => { - let (ind, exercise) = find_exercise(&name, exercises)?; + app_state.set_current_exercise_by_name(&name)?; + app_state.set_pending(app_state.current_exercise_ind())?; + let exercise = app_state.current_exercise(); exercise.reset()?; - state_file.reset(ind)?; println!("The exercise {exercise} has been reset!"); } Some(Subcommands::Hint { name }) => { - let (_, exercise) = find_exercise(&name, exercises)?; - println!("{}", exercise.hint); + app_state.set_current_exercise_by_name(&name)?; + println!("{}", app_state.current_exercise().hint); } - Some(Subcommands::Verify) => match verify(exercises, 0)? { - VerifyState::AllExercisesDone => println!("All exercises done!"), - VerifyState::Failed(exercise) => bail!("Exercise {exercise} failed"), - }, } Ok(()) diff --git a/src/run.rs b/src/run.rs index 2fd6f40..18da193 100644 --- a/src/run.rs +++ b/src/run.rs @@ -2,13 +2,10 @@ use anyhow::{bail, Result}; use crossterm::style::Stylize; use std::io::{stdout, Write}; -use crate::exercise::Exercise; +use crate::app_state::{AppState, ExercisesProgress}; -// Invoke the rust compiler on the path of the given exercise, -// and run the ensuing binary. -// The verbose argument helps determine whether or not to show -// the output from the test harnesses (if the mode of the exercise is test) -pub fn run(exercise: &Exercise) -> Result<()> { +pub fn run(app_state: &mut AppState) -> Result<()> { + let exercise = app_state.current_exercise(); let output = exercise.run()?; { @@ -22,7 +19,19 @@ pub fn run(exercise: &Exercise) -> Result<()> { bail!("Ran {exercise} with errors"); } - println!("{}", "✓ Successfully ran {exercise}".green()); + println!( + "{}{}", + "✓ Successfully ran ".green(), + exercise.path.to_string_lossy().green(), + ); + + match app_state.done_current_exercise()? { + ExercisesProgress::AllDone => println!( + "🎉 Congratulations! You have done all the exercises! +🔚 There are no more exercises to do next!" + ), + ExercisesProgress::Pending => println!("Next exercise: {}", app_state.current_exercise()), + } Ok(()) } diff --git a/src/state_file.rs b/src/state_file.rs deleted file mode 100644 index 6b80354..0000000 --- a/src/state_file.rs +++ /dev/null @@ -1,68 +0,0 @@ -use anyhow::{bail, Context, Result}; -use serde::{Deserialize, Serialize}; -use std::fs; - -use crate::exercise::Exercise; - -#[derive(Serialize, Deserialize)] -#[serde(deny_unknown_fields)] -pub struct StateFile { - next_exercise_ind: usize, - progress: Vec, -} - -const BAD_INDEX_ERR: &str = "The next exercise index is higher than the number of exercises"; - -impl StateFile { - fn read(exercises: &[Exercise]) -> Option { - let file_content = fs::read(".rustlings-state.json").ok()?; - - let slf: Self = serde_json::de::from_slice(&file_content).ok()?; - - if slf.progress.len() != exercises.len() || slf.next_exercise_ind >= exercises.len() { - return None; - } - - Some(slf) - } - - pub fn read_or_default(exercises: &[Exercise]) -> Self { - Self::read(exercises).unwrap_or_else(|| Self { - next_exercise_ind: 0, - progress: vec![false; exercises.len()], - }) - } - - fn write(&self) -> Result<()> { - let mut buf = Vec::with_capacity(1024); - serde_json::ser::to_writer(&mut buf, self).context("Failed to serialize the state")?; - fs::write(".rustlings-state.json", buf) - .context("Failed to write the state file `.rustlings-state.json`")?; - - Ok(()) - } - - #[inline] - pub fn next_exercise_ind(&self) -> usize { - self.next_exercise_ind - } - - pub fn set_next_exercise_ind(&mut self, ind: usize) -> Result<()> { - if ind >= self.progress.len() { - bail!(BAD_INDEX_ERR); - } - self.next_exercise_ind = ind; - self.write() - } - - #[inline] - pub fn progress(&self) -> &[bool] { - &self.progress - } - - pub fn reset(&mut self, ind: usize) -> Result<()> { - let done = self.progress.get_mut(ind).context(BAD_INDEX_ERR)?; - *done = false; - self.write() - } -} diff --git a/src/verify.rs b/src/verify.rs deleted file mode 100644 index cea6bdf..0000000 --- a/src/verify.rs +++ /dev/null @@ -1,85 +0,0 @@ -use anyhow::Result; -use crossterm::style::{Attribute, ContentStyle, Stylize}; -use std::io::{stdout, Write}; - -use crate::exercise::{Exercise, Mode, State}; - -pub enum VerifyState { - AllExercisesDone, - Failed(&'static Exercise), -} - -// Verify that the provided container of Exercise objects -// can be compiled and run without any failures. -// Any such failures will be reported to the end user. -// If the Exercise being verified is a test, the verbose boolean -// determines whether or not the test harness outputs are displayed. -pub fn verify( - exercises: &'static [Exercise], - mut current_exercise_ind: usize, -) -> Result { - while current_exercise_ind < exercises.len() { - let exercise = &exercises[current_exercise_ind]; - - println!( - "Progress: {current_exercise_ind}/{} ({:.1}%)\n", - exercises.len(), - current_exercise_ind as f32 / exercises.len() as f32 * 100.0, - ); - - let output = exercise.run()?; - - { - let mut stdout = stdout().lock(); - stdout.write_all(&output.stdout)?; - stdout.write_all(&output.stderr)?; - stdout.flush()?; - } - - if !output.status.success() { - return Ok(VerifyState::Failed(exercise)); - } - - println!(); - // TODO: Color - match exercise.mode { - Mode::Compile => println!("Successfully ran {exercise}!"), - Mode::Test => println!("Successfully tested {exercise}!"), - Mode::Clippy => println!("Successfully checked {exercise}!"), - } - - if let State::Pending(context) = exercise.state()? { - println!( - "\nYou can keep working on this exercise, -or jump into the next one by removing the {} comment:\n", - "`I AM NOT DONE`".bold() - ); - - for context_line in context { - let formatted_line = if context_line.important { - format!("{}", context_line.line.bold()) - } else { - context_line.line - }; - - println!( - "{:>2} {} {}", - ContentStyle { - foreground_color: Some(crossterm::style::Color::Blue), - background_color: None, - underline_color: None, - attributes: Attribute::Bold.into() - } - .apply(context_line.number), - "|".blue(), - formatted_line, - ); - } - return Ok(VerifyState::Failed(exercise)); - } - - current_exercise_ind += 1; - } - - Ok(VerifyState::AllExercisesDone) -} diff --git a/src/watch.rs b/src/watch.rs index b29169b..929275f 100644 --- a/src/watch.rs +++ b/src/watch.rs @@ -15,7 +15,7 @@ mod debounce_event; mod state; mod terminal_event; -use crate::{exercise::Exercise, state_file::StateFile}; +use crate::app_state::AppState; use self::{ debounce_event::DebounceEventHandler, @@ -39,23 +39,23 @@ pub enum WatchExit { List, } -pub fn watch(state_file: &mut StateFile, exercises: &'static [Exercise]) -> Result { +pub fn watch(app_state: &mut AppState) -> Result { let (tx, rx) = channel(); let mut debouncer = new_debouncer( Duration::from_secs(1), DebounceEventHandler { tx: tx.clone(), - exercises, + exercises: app_state.exercises(), }, )?; debouncer .watcher() .watch(Path::new("exercises"), RecursiveMode::Recursive)?; - let mut watch_state = WatchState::new(state_file, exercises); + let mut watch_state = WatchState::new(app_state); // TODO: bool - watch_state.run_exercise()?; + watch_state.run_current_exercise()?; watch_state.render()?; thread::spawn(move || terminal_event_handler(tx)); diff --git a/src/watch/state.rs b/src/watch/state.rs index 6f6d2f1..a7647d8 100644 --- a/src/watch/state.rs +++ b/src/watch/state.rs @@ -1,26 +1,16 @@ -use anyhow::{Context, Result}; +use anyhow::Result; use crossterm::{ - style::{Attribute, ContentStyle, Stylize}, + style::Stylize, terminal::{size, Clear, ClearType}, ExecutableCommand, }; -use std::{ - fmt::Write as _, - io::{self, StdoutLock, Write}, -}; +use std::io::{self, StdoutLock, Write}; -use crate::{ - exercise::{Exercise, State}, - progress_bar::progress_bar, - state_file::StateFile, -}; +use crate::{app_state::AppState, progress_bar::progress_bar}; pub struct WatchState<'a> { writer: StdoutLock<'a>, - exercises: &'static [Exercise], - exercise: &'static Exercise, - current_exercise_ind: usize, - progress: u16, + app_state: &'a mut AppState, stdout: Option>, stderr: Option>, message: Option, @@ -28,19 +18,12 @@ pub struct WatchState<'a> { } impl<'a> WatchState<'a> { - pub fn new(state_file: &StateFile, exercises: &'static [Exercise]) -> Self { - let current_exercise_ind = state_file.next_exercise_ind(); - let progress = state_file.progress().iter().filter(|done| **done).count() as u16; - let exercise = &exercises[current_exercise_ind]; - + pub fn new(app_state: &'a mut AppState) -> Self { let writer = io::stdout().lock(); Self { writer, - exercises, - exercise, - current_exercise_ind, - progress, + app_state, stdout: None, stderr: None, message: None, @@ -53,8 +36,8 @@ impl<'a> WatchState<'a> { self.writer } - pub fn run_exercise(&mut self) -> Result { - let output = self.exercise.run()?; + pub fn run_current_exercise(&mut self) -> Result { + let output = self.app_state.current_exercise().run()?; self.stdout = Some(output.stdout); if !output.status.success() { @@ -64,55 +47,15 @@ impl<'a> WatchState<'a> { self.stderr = None; - if let State::Pending(context) = self.exercise.state()? { - let mut message = format!( - " -You can keep working on this exercise or jump into the next one by removing the {} comment: - -", - "`I AM NOT DONE`".bold(), - ); - - for context_line in context { - let formatted_line = if context_line.important { - context_line.line.bold() - } else { - context_line.line.stylize() - }; - - writeln!( - message, - "{:>2} {} {}", - ContentStyle { - foreground_color: Some(crossterm::style::Color::Blue), - background_color: None, - underline_color: None, - attributes: Attribute::Bold.into() - } - .apply(context_line.number), - "|".blue(), - formatted_line, - )?; - } - - self.message = Some(message); - return Ok(false); - } - Ok(true) } pub fn run_exercise_with_ind(&mut self, exercise_ind: usize) -> Result { - self.exercise = self - .exercises - .get(exercise_ind) - .context("Invalid exercise index")?; - self.current_exercise_ind = exercise_ind; - - self.run_exercise() + self.app_state.set_current_exercise_ind(exercise_ind)?; + self.run_current_exercise() } - pub fn show_prompt(&mut self) -> io::Result<()> { + fn show_prompt(&mut self) -> io::Result<()> { self.writer.write_all(b"\n\n")?; if !self.hint_displayed { @@ -150,18 +93,27 @@ You can keep working on this exercise or jump into the next one by removing the if self.hint_displayed { self.writer .write_fmt(format_args!("\n{}\n", "Hint".bold().cyan().underlined()))?; - self.writer.write_all(self.exercise.hint.as_bytes())?; + self.writer + .write_all(self.app_state.current_exercise().hint.as_bytes())?; self.writer.write_all(b"\n\n")?; } let line_width = size()?.0; - let progress_bar = progress_bar(self.progress, self.exercises.len() as u16, line_width)?; + let progress_bar = progress_bar( + self.app_state.n_done(), + self.app_state.exercises().len() as u16, + line_width, + )?; self.writer.write_all(progress_bar.as_bytes())?; self.writer.write_all(b"Current exercise: ")?; self.writer.write_fmt(format_args!( "{}", - self.exercise.path.to_string_lossy().bold() + self.app_state + .current_exercise() + .path + .to_string_lossy() + .bold(), ))?; self.show_prompt()?; diff --git a/tests/fixture/state/exercises/pending_exercise.rs b/tests/fixture/state/exercises/pending_exercise.rs index f579d0b..016b827 100644 --- a/tests/fixture/state/exercises/pending_exercise.rs +++ b/tests/fixture/state/exercises/pending_exercise.rs @@ -1,7 +1,5 @@ // fake_exercise -// I AM NOT DONE - fn main() { } diff --git a/tests/fixture/state/exercises/pending_test_exercise.rs b/tests/fixture/state/exercises/pending_test_exercise.rs index 8756f02..2002ef1 100644 --- a/tests/fixture/state/exercises/pending_test_exercise.rs +++ b/tests/fixture/state/exercises/pending_test_exercise.rs @@ -1,4 +1,2 @@ -// I AM NOT DONE - #[test] fn it_works() {} diff --git a/tests/integration_tests.rs b/tests/integration_tests.rs index f8f4383..51cdefb 100644 --- a/tests/integration_tests.rs +++ b/tests/integration_tests.rs @@ -1,7 +1,6 @@ use assert_cmd::prelude::*; -use glob::glob; use predicates::boolean::PredicateBooleanExt; -use std::{fs::File, io::Read, process::Command}; +use std::process::Command; #[test] fn fails_when_in_wrong_dir() { @@ -137,31 +136,6 @@ fn get_hint_for_single_test() { .stdout("Hello!\n"); } -#[test] -fn all_exercises_require_confirmation() { - for exercise in glob("exercises/**/*.rs").unwrap() { - let path = exercise.unwrap(); - if path.file_name().unwrap() == "mod.rs" { - continue; - } - let source = { - let mut file = File::open(&path).unwrap(); - let mut s = String::new(); - file.read_to_string(&mut s).unwrap(); - s - }; - source - .matches("// I AM NOT DONE") - .next() - .unwrap_or_else(|| { - panic!( - "There should be an `I AM NOT DONE` annotation in {:?}", - path - ) - }); - } -} - #[test] fn run_compile_exercise_does_not_prompt() { Command::cargo_bin("rustlings") -- cgit v1.2.3 From 65849629f5877a5d9f51accbb593d431938bd60c Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 11 Apr 2024 02:51:23 +0200 Subject: Remove glob --- Cargo.lock | 7 ------- Cargo.toml | 1 - 2 files changed, 8 deletions(-) (limited to 'Cargo.lock') diff --git a/Cargo.lock b/Cargo.lock index aeb6c61..a8ffb8e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -320,12 +320,6 @@ dependencies = [ "toml_edit", ] -[[package]] -name = "glob" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" - [[package]] name = "hashbrown" version = "0.14.3" @@ -690,7 +684,6 @@ dependencies = [ "assert_cmd", "clap", "crossterm", - "glob", "notify-debouncer-mini", "predicates", "ratatui", diff --git a/Cargo.toml b/Cargo.toml index 435dfd4..83f01c2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -47,7 +47,6 @@ which = "6.0.1" [dev-dependencies] assert_cmd = "2.0.14" -glob = "0.3.0" predicates = "3.1.0" [profile.release] -- cgit v1.2.3 From c3933904f643238eaafe42e7da967c8262fef22a Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 11 Apr 2024 02:51:50 +0200 Subject: Update deps --- Cargo.lock | 8 ++++---- Cargo.toml | 2 +- rustlings-macros/Cargo.toml | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) (limited to 'Cargo.lock') diff --git a/Cargo.lock b/Cargo.lock index a8ffb8e..554db28 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -79,9 +79,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.81" +version = "1.0.82" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0952808a6c2afd1aa8947271f3a60f1a6763c7b912d210184c5149b5cf147247" +checksum = "f538837af36e6f6a9be0faa67f9a314f8119e4e4b5867c6ab40ed60360142519" [[package]] name = "assert_cmd" @@ -598,9 +598,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.35" +version = "1.0.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" +checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" dependencies = [ "proc-macro2", ] diff --git a/Cargo.toml b/Cargo.toml index 83f01c2..285e7df 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,7 +20,7 @@ license = "MIT" edition = "2021" [workspace.dependencies] -anyhow = "1.0.81" +anyhow = "1.0.82" serde = { version = "1.0.197", features = ["derive"] } toml_edit = { version = "0.22.9", default-features = false, features = ["parse", "serde"] } diff --git a/rustlings-macros/Cargo.toml b/rustlings-macros/Cargo.toml index 0114c8f..79279f5 100644 --- a/rustlings-macros/Cargo.toml +++ b/rustlings-macros/Cargo.toml @@ -9,4 +9,4 @@ edition.workspace = true proc-macro = true [dependencies] -quote = "1.0.35" +quote = "1.0.36" -- cgit v1.2.3 From 1e3745ccdf5ca41ae47d4f4d8594e8070df200a5 Mon Sep 17 00:00:00 2001 From: mo8it Date: Fri, 12 Apr 2024 00:58:26 +0200 Subject: Update winnow --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'Cargo.lock') diff --git a/Cargo.lock b/Cargo.lock index 554db28..a5ad8c9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1130,9 +1130,9 @@ checksum = "32b752e52a2da0ddfbdbcc6fceadfeede4c939ed16d13e648833a61dfb611ed8" [[package]] name = "winnow" -version = "0.6.5" +version = "0.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dffa400e67ed5a4dd237983829e66475f0a4a26938c4b04c21baede6262215b8" +checksum = "f0c976aaaa0e1f90dbb21e9587cdaf1d9679a1cde8875c0d6bd83ab96a208352" dependencies = [ "memchr", ] -- cgit v1.2.3 From 98c5088a39439389a4e198839b47819bfa1b1712 Mon Sep 17 00:00:00 2001 From: mo8it Date: Fri, 12 Apr 2024 14:52:50 +0200 Subject: Update deps --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'Cargo.lock') diff --git a/Cargo.lock b/Cargo.lock index a5ad8c9..6c64661 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -25,9 +25,9 @@ dependencies = [ [[package]] name = "allocator-api2" -version = "0.2.16" +version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0942ffc6dcaadf03badf6e6a2d0228460359d5e34b57ccdc720b7382dfbd5ec5" +checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f" [[package]] name = "anstream" -- cgit v1.2.3 From 5c0073a9485c4226e58b657cb49628919a28a942 Mon Sep 17 00:00:00 2001 From: mo8it Date: Sun, 14 Apr 2024 01:15:43 +0200 Subject: Tolerate changes in the state file --- Cargo.lock | 1 + Cargo.toml | 1 + exercises/00_intro/intro1.rs | 1 - info.toml | 272 +++++++++++++++++++++---------------------- src/app_state.rs | 207 +++++++++++++++----------------- src/app_state/state_file.rs | 112 ++++++++++++++++++ src/exercise.rs | 72 +++--------- src/info_file.rs | 81 +++++++++++++ src/init.rs | 23 ++-- src/list.rs | 11 +- src/list/state.rs | 35 +++--- src/main.rs | 40 ++++--- src/run.rs | 2 +- src/watch.rs | 15 ++- src/watch/debounce_event.rs | 44 ------- src/watch/notify_event.rs | 42 +++++++ 16 files changed, 552 insertions(+), 407 deletions(-) create mode 100644 src/app_state/state_file.rs create mode 100644 src/info_file.rs delete mode 100644 src/watch/debounce_event.rs create mode 100644 src/watch/notify_event.rs (limited to 'Cargo.lock') diff --git a/Cargo.lock b/Cargo.lock index 6c64661..dbf1923 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -684,6 +684,7 @@ dependencies = [ "assert_cmd", "clap", "crossterm", + "hashbrown", "notify-debouncer-mini", "predicates", "ratatui", diff --git a/Cargo.toml b/Cargo.toml index 285e7df..14ae9a1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -37,6 +37,7 @@ edition.workspace = true anyhow.workspace = true clap = { version = "4.5.4", features = ["derive"] } crossterm = "0.27.0" +hashbrown = "0.14.3" notify-debouncer-mini = "0.4.1" ratatui = "0.26.1" rustlings-macros = { path = "rustlings-macros" } diff --git a/exercises/00_intro/intro1.rs b/exercises/00_intro/intro1.rs index e4e0444..170d195 100644 --- a/exercises/00_intro/intro1.rs +++ b/exercises/00_intro/intro1.rs @@ -1,6 +1,5 @@ // intro1.rs // -// TODO: Update comment // 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, remove the `I AM NOT DONE` comment below. diff --git a/info.toml b/info.toml index b6b6800..fa90ad7 100644 --- a/info.toml +++ b/info.toml @@ -33,10 +33,11 @@ https://github.com/rust-lang/rustlings/blob/main/CONTRIBUTING.md # INTRO +# TODO: Update exercise [[exercises]] name = "intro1" -path = "exercises/00_intro/intro1.rs" -mode = "compile" +dir = "00_intro" +mode = "run" # TODO: Fix hint hint = """ Remove the `I AM NOT DONE` comment in the `exercises/intro00/intro1.rs` file @@ -44,8 +45,8 @@ to move on to the next exercise.""" [[exercises]] name = "intro2" -path = "exercises/00_intro/intro2.rs" -mode = "compile" +dir = "00_intro" +mode = "run" hint = """ The compiler is informing us that we've got the name of the print macro wrong, and has suggested an alternative.""" @@ -53,16 +54,16 @@ The compiler is informing us that we've got the name of the print macro wrong, a [[exercises]] name = "variables1" -path = "exercises/01_variables/variables1.rs" -mode = "compile" +dir = "01_variables" +mode = "run" hint = """ The declaration in the first line in the main function is missing a keyword that is needed in Rust to create a new variable binding.""" [[exercises]] name = "variables2" -path = "exercises/01_variables/variables2.rs" -mode = "compile" +dir = "01_variables" +mode = "run" hint = """ The compiler message is saying that Rust cannot infer the type that the variable binding `x` has with what is given here. @@ -80,8 +81,8 @@ What if `x` is the same type as `10`? What if it's a different type?""" [[exercises]] name = "variables3" -path = "exercises/01_variables/variables3.rs" -mode = "compile" +dir = "01_variables" +mode = "run" hint = """ Oops! In this exercise, we have a variable binding that we've created on in the first line in the `main` function, and we're trying to use it in the next line, @@ -94,8 +95,8 @@ programming language -- thankfully the Rust compiler has caught this for us!""" [[exercises]] name = "variables4" -path = "exercises/01_variables/variables4.rs" -mode = "compile" +dir = "01_variables" +mode = "run" hint = """ In Rust, variable bindings are immutable by default. But here we're trying to reassign a different value to `x`! There's a keyword we can use to make @@ -103,8 +104,8 @@ a variable binding mutable instead.""" [[exercises]] name = "variables5" -path = "exercises/01_variables/variables5.rs" -mode = "compile" +dir = "01_variables" +mode = "run" hint = """ In `variables4` we already learned how to make an immutable variable mutable using a special keyword. Unfortunately this doesn't help us much in this @@ -121,8 +122,8 @@ Try to solve this exercise afterwards using this technique.""" [[exercises]] name = "variables6" -path = "exercises/01_variables/variables6.rs" -mode = "compile" +dir = "01_variables" +mode = "run" hint = """ We know about variables and mutability, but there is another important type of variable available: constants. @@ -141,8 +142,8 @@ https://doc.rust-lang.org/book/ch03-01-variables-and-mutability.html#constants [[exercises]] name = "functions1" -path = "exercises/02_functions/functions1.rs" -mode = "compile" +dir = "02_functions" +mode = "run" hint = """ This main function is calling a function that it expects to exist, but the function doesn't exist. It expects this function to have the name `call_me`. @@ -151,24 +152,24 @@ Sounds a lot like `main`, doesn't it?""" [[exercises]] name = "functions2" -path = "exercises/02_functions/functions2.rs" -mode = "compile" +dir = "02_functions" +mode = "run" hint = """ Rust requires that all parts of a function's signature have type annotations, but `call_me` is missing the type annotation of `num`.""" [[exercises]] name = "functions3" -path = "exercises/02_functions/functions3.rs" -mode = "compile" +dir = "02_functions" +mode = "run" hint = """ This time, the function *declaration* is okay, but there's something wrong with the place where we're calling the function.""" [[exercises]] name = "functions4" -path = "exercises/02_functions/functions4.rs" -mode = "compile" +dir = "02_functions" +mode = "run" hint = """ The error message points to the function `sale_price` and says it expects a type after the `->`. This is where the function's return type should be -- take a @@ -179,8 +180,8 @@ for the inputs of the functions here, since the original prices shouldn't be neg [[exercises]] name = "functions5" -path = "exercises/02_functions/functions5.rs" -mode = "compile" +dir = "02_functions" +mode = "run" hint = """ This is a really common error that can be fixed by removing one character. It happens because Rust distinguishes between expressions and statements: @@ -198,7 +199,7 @@ They are not the same. There are two solutions: [[exercises]] name = "if1" -path = "exercises/03_if/if1.rs" +dir = "03_if" mode = "test" hint = """ It's possible to do this in one line if you would like! @@ -214,7 +215,7 @@ Remember in Rust that: [[exercises]] name = "if2" -path = "exercises/03_if/if2.rs" +dir = "03_if" mode = "test" hint = """ For that first compiler error, it's important in Rust that each conditional @@ -223,7 +224,7 @@ conditions checking different input values.""" [[exercises]] name = "if3" -path = "exercises/03_if/if3.rs" +dir = "03_if" mode = "test" hint = """ In Rust, every arm of an `if` expression has to return the same type of value. @@ -233,7 +234,6 @@ Make sure the type is consistent across all arms.""" [[exercises]] name = "quiz1" -path = "exercises/quiz1.rs" mode = "test" hint = "No hints this time ;)" @@ -241,20 +241,20 @@ hint = "No hints this time ;)" [[exercises]] name = "primitive_types1" -path = "exercises/04_primitive_types/primitive_types1.rs" -mode = "compile" +dir = "04_primitive_types" +mode = "run" hint = "No hints this time ;)" [[exercises]] name = "primitive_types2" -path = "exercises/04_primitive_types/primitive_types2.rs" -mode = "compile" +dir = "04_primitive_types" +mode = "run" hint = "No hints this time ;)" [[exercises]] name = "primitive_types3" -path = "exercises/04_primitive_types/primitive_types3.rs" -mode = "compile" +dir = "04_primitive_types" +mode = "run" hint = """ There's a shorthand to initialize Arrays with a certain size that does not require you to type in 100 items (but you certainly can if you want!). @@ -269,7 +269,7 @@ for `a.len() >= 100`?""" [[exercises]] name = "primitive_types4" -path = "exercises/04_primitive_types/primitive_types4.rs" +dir = "04_primitive_types" mode = "test" hint = """ Take a look at the 'Understanding Ownership -> Slices -> Other Slices' section @@ -284,8 +284,8 @@ https://doc.rust-lang.org/nomicon/coercions.html""" [[exercises]] name = "primitive_types5" -path = "exercises/04_primitive_types/primitive_types5.rs" -mode = "compile" +dir = "04_primitive_types" +mode = "run" hint = """ Take a look at the 'Data Types -> The Tuple Type' section of the book: https://doc.rust-lang.org/book/ch03-02-data-types.html#the-tuple-type @@ -297,7 +297,7 @@ of the tuple. You can do it!!""" [[exercises]] name = "primitive_types6" -path = "exercises/04_primitive_types/primitive_types6.rs" +dir = "04_primitive_types" mode = "test" hint = """ While you could use a destructuring `let` for the tuple here, try @@ -310,7 +310,7 @@ Now you have another tool in your toolbox!""" [[exercises]] name = "vecs1" -path = "exercises/05_vecs/vecs1.rs" +dir = "05_vecs" mode = "test" hint = """ In Rust, there are two ways to define a Vector. @@ -325,7 +325,7 @@ of the Rust book to learn more. [[exercises]] name = "vecs2" -path = "exercises/05_vecs/vecs2.rs" +dir = "05_vecs" mode = "test" hint = """ In the first function we are looping over the Vector and getting a reference to @@ -348,7 +348,7 @@ What do you think is the more commonly used pattern under Rust developers? [[exercises]] name = "move_semantics1" -path = "exercises/06_move_semantics/move_semantics1.rs" +dir = "06_move_semantics" mode = "test" hint = """ So you've got the "cannot borrow immutable local variable `vec` as mutable" @@ -362,7 +362,7 @@ happens!""" [[exercises]] name = "move_semantics2" -path = "exercises/06_move_semantics/move_semantics2.rs" +dir = "06_move_semantics" mode = "test" hint = """ When running this exercise for the first time, you'll notice an error about @@ -383,7 +383,7 @@ try them all: [[exercises]] name = "move_semantics3" -path = "exercises/06_move_semantics/move_semantics3.rs" +dir = "06_move_semantics" mode = "test" hint = """ The difference between this one and the previous ones is that the first line @@ -393,7 +393,7 @@ an existing binding to be a mutable binding instead of an immutable one :)""" [[exercises]] name = "move_semantics4" -path = "exercises/06_move_semantics/move_semantics4.rs" +dir = "06_move_semantics" mode = "test" hint = """ Stop reading whenever you feel like you have enough direction :) Or try @@ -407,7 +407,7 @@ So the end goal is to: [[exercises]] name = "move_semantics5" -path = "exercises/06_move_semantics/move_semantics5.rs" +dir = "06_move_semantics" mode = "test" hint = """ Carefully reason about the range in which each mutable reference is in @@ -419,8 +419,8 @@ https://doc.rust-lang.org/book/ch04-02-references-and-borrowing.html#mutable-ref [[exercises]] name = "move_semantics6" -path = "exercises/06_move_semantics/move_semantics6.rs" -mode = "compile" +dir = "06_move_semantics" +mode = "run" hint = """ To find the answer, you can consult the book section "References and Borrowing": https://doc.rust-lang.org/stable/book/ch04-02-references-and-borrowing.html @@ -440,7 +440,7 @@ Another hint: it has to do with the `&` character.""" [[exercises]] name = "structs1" -path = "exercises/07_structs/structs1.rs" +dir = "07_structs" mode = "test" hint = """ Rust has more than one type of struct. Three actually, all variants are used to @@ -460,7 +460,7 @@ https://doc.rust-lang.org/book/ch05-01-defining-structs.html""" [[exercises]] name = "structs2" -path = "exercises/07_structs/structs2.rs" +dir = "07_structs" mode = "test" hint = """ Creating instances of structs is easy, all you need to do is assign some values @@ -472,7 +472,7 @@ https://doc.rust-lang.org/stable/book/ch05-01-defining-structs.html#creating-ins [[exercises]] name = "structs3" -path = "exercises/07_structs/structs3.rs" +dir = "07_structs" mode = "test" hint = """ For `is_international`: What makes a package international? Seems related to @@ -488,21 +488,21 @@ https://doc.rust-lang.org/book/ch05-03-method-syntax.html""" [[exercises]] name = "enums1" -path = "exercises/08_enums/enums1.rs" -mode = "compile" +dir = "08_enums" +mode = "run" hint = "No hints this time ;)" [[exercises]] name = "enums2" -path = "exercises/08_enums/enums2.rs" -mode = "compile" +dir = "08_enums" +mode = "run" hint = """ You can create enumerations that have different variants with different types such as no data, anonymous structs, a single string, tuples, ...etc""" [[exercises]] name = "enums3" -path = "exercises/08_enums/enums3.rs" +dir = "08_enums" mode = "test" hint = """ As a first step, you can define enums to compile this code without errors. @@ -516,8 +516,8 @@ to get value in the variant.""" [[exercises]] name = "strings1" -path = "exercises/09_strings/strings1.rs" -mode = "compile" +dir = "09_strings" +mode = "run" hint = """ The `current_favorite_color` function is currently returning a string slice with the `'static` lifetime. We know this because the data of the string lives @@ -530,8 +530,8 @@ another way that uses the `From` trait.""" [[exercises]] name = "strings2" -path = "exercises/09_strings/strings2.rs" -mode = "compile" +dir = "09_strings" +mode = "run" hint = """ Yes, it would be really easy to fix this by just changing the value bound to `word` to be a string slice instead of a `String`, wouldn't it?? There is a way @@ -545,7 +545,7 @@ https://doc.rust-lang.org/stable/book/ch15-02-deref.html#implicit-deref-coercion [[exercises]] name = "strings3" -path = "exercises/09_strings/strings3.rs" +dir = "09_strings" mode = "test" hint = """ There's tons of useful standard library functions for strings. Let's try and use some of them: @@ -556,16 +556,16 @@ the string slice into an owned string, which you can then freely extend.""" [[exercises]] name = "strings4" -path = "exercises/09_strings/strings4.rs" -mode = "compile" +dir = "09_strings" +mode = "run" hint = "No hints this time ;)" # MODULES [[exercises]] name = "modules1" -path = "exercises/10_modules/modules1.rs" -mode = "compile" +dir = "10_modules" +mode = "run" hint = """ Everything is private in Rust by default-- but there's a keyword we can use to make something public! The compiler error should point to the thing that @@ -573,8 +573,8 @@ needs to be public.""" [[exercises]] name = "modules2" -path = "exercises/10_modules/modules2.rs" -mode = "compile" +dir = "10_modules" +mode = "run" hint = """ The delicious_snacks module is trying to present an external interface that is different than its internal structure (the `fruits` and `veggies` modules and @@ -585,8 +585,8 @@ Learn more at https://doc.rust-lang.org/book/ch07-04-bringing-paths-into-scope-w [[exercises]] name = "modules3" -path = "exercises/10_modules/modules3.rs" -mode = "compile" +dir = "10_modules" +mode = "run" hint = """ `UNIX_EPOCH` and `SystemTime` are declared in the `std::time` module. Add a `use` statement for these two to bring them into scope. You can use nested @@ -596,7 +596,7 @@ paths or the glob operator to bring these two in using only one line.""" [[exercises]] name = "hashmaps1" -path = "exercises/11_hashmaps/hashmaps1.rs" +dir = "11_hashmaps" mode = "test" hint = """ Hint 1: Take a look at the return type of the function to figure out @@ -608,7 +608,7 @@ Hint 2: Number of fruits should be at least 5. And you have to put [[exercises]] name = "hashmaps2" -path = "exercises/11_hashmaps/hashmaps2.rs" +dir = "11_hashmaps" mode = "test" hint = """ Use the `entry()` and `or_insert()` methods of `HashMap` to achieve this. @@ -617,7 +617,7 @@ Learn more at https://doc.rust-lang.org/stable/book/ch08-03-hash-maps.html#only- [[exercises]] name = "hashmaps3" -path = "exercises/11_hashmaps/hashmaps3.rs" +dir = "11_hashmaps" mode = "test" hint = """ Hint 1: Use the `entry()` and `or_insert()` methods of `HashMap` to insert @@ -635,7 +635,6 @@ Learn more at https://doc.rust-lang.org/book/ch08-03-hash-maps.html#updating-a-v [[exercises]] name = "quiz2" -path = "exercises/quiz2.rs" mode = "test" hint = "No hints this time ;)" @@ -643,7 +642,7 @@ hint = "No hints this time ;)" [[exercises]] name = "options1" -path = "exercises/12_options/options1.rs" +dir = "12_options" mode = "test" hint = """ Options can have a `Some` value, with an inner value, or a `None` value, @@ -655,7 +654,7 @@ it doesn't panic in your face later?""" [[exercises]] name = "options2" -path = "exercises/12_options/options2.rs" +dir = "12_options" mode = "test" hint = """ Check out: @@ -672,8 +671,8 @@ Also see `Option::flatten` [[exercises]] name = "options3" -path = "exercises/12_options/options3.rs" -mode = "compile" +dir = "12_options" +mode = "run" hint = """ The compiler says a partial move happened in the `match` statement. How can this be avoided? The compiler shows the correction needed. @@ -685,7 +684,7 @@ https://doc.rust-lang.org/std/keyword.ref.html""" [[exercises]] name = "errors1" -path = "exercises/13_error_handling/errors1.rs" +dir = "13_error_handling" mode = "test" hint = """ `Ok` and `Err` are the two variants of `Result`, so what the tests are saying @@ -701,7 +700,7 @@ To make this change, you'll need to: [[exercises]] name = "errors2" -path = "exercises/13_error_handling/errors2.rs" +dir = "13_error_handling" mode = "test" hint = """ One way to handle this is using a `match` statement on @@ -717,8 +716,8 @@ and give it a try!""" [[exercises]] name = "errors3" -path = "exercises/13_error_handling/errors3.rs" -mode = "compile" +dir = "13_error_handling" +mode = "run" hint = """ If other functions can return a `Result`, why shouldn't `main`? It's a fairly common convention to return something like `Result<(), ErrorType>` from your @@ -729,7 +728,7 @@ positive results.""" [[exercises]] name = "errors4" -path = "exercises/13_error_handling/errors4.rs" +dir = "13_error_handling" mode = "test" hint = """ `PositiveNonzeroInteger::new` is always creating a new instance and returning @@ -741,8 +740,8 @@ everything is... okay :)""" [[exercises]] name = "errors5" -path = "exercises/13_error_handling/errors5.rs" -mode = "compile" +dir = "13_error_handling" +mode = "run" hint = """ There are two different possible `Result` types produced within `main()`, which are propagated using `?` operators. How do we declare a return type from @@ -765,7 +764,7 @@ https://doc.rust-lang.org/stable/rust-by-example/error/multiple_error_types/reen [[exercises]] name = "errors6" -path = "exercises/13_error_handling/errors6.rs" +dir = "13_error_handling" mode = "test" hint = """ This exercise uses a completed version of `PositiveNonzeroInteger` from @@ -787,8 +786,8 @@ https://doc.rust-lang.org/std/result/enum.Result.html#method.map_err""" [[exercises]] name = "generics1" -path = "exercises/14_generics/generics1.rs" -mode = "compile" +dir = "14_generics" +mode = "run" hint = """ Vectors in Rust make use of generics to create dynamically sized arrays of any type. @@ -797,7 +796,7 @@ You need to tell the compiler what type we are pushing onto this vector.""" [[exercises]] name = "generics2" -path = "exercises/14_generics/generics2.rs" +dir = "14_generics" mode = "test" hint = """ Currently we are wrapping only values of type `u32`. @@ -811,7 +810,7 @@ If you are still stuck https://doc.rust-lang.org/stable/book/ch10-01-syntax.html [[exercises]] name = "traits1" -path = "exercises/15_traits/traits1.rs" +dir = "15_traits" mode = "test" hint = """ A discussion about Traits in Rust can be found at: @@ -820,7 +819,7 @@ https://doc.rust-lang.org/book/ch10-02-traits.html [[exercises]] name = "traits2" -path = "exercises/15_traits/traits2.rs" +dir = "15_traits" mode = "test" hint = """ Notice how the trait takes ownership of `self`, and returns `Self`. @@ -833,7 +832,7 @@ the documentation at: https://doc.rust-lang.org/std/vec/struct.Vec.html""" [[exercises]] name = "traits3" -path = "exercises/15_traits/traits3.rs" +dir = "15_traits" mode = "test" hint = """ Traits can have a default implementation for functions. Structs that implement @@ -845,7 +844,7 @@ See the documentation at: https://doc.rust-lang.org/book/ch10-02-traits.html#def [[exercises]] name = "traits4" -path = "exercises/15_traits/traits4.rs" +dir = "15_traits" mode = "test" hint = """ Instead of using concrete types as parameters you can use traits. Try replacing @@ -856,8 +855,8 @@ See the documentation at: https://doc.rust-lang.org/book/ch10-02-traits.html#tra [[exercises]] name = "traits5" -path = "exercises/15_traits/traits5.rs" -mode = "compile" +dir = "15_traits" +mode = "run" hint = """ To ensure a parameter implements multiple traits use the '+ syntax'. Try replacing the '??' with 'impl <> + <>'. @@ -869,7 +868,6 @@ See the documentation at: https://doc.rust-lang.org/book/ch10-02-traits.html#spe [[exercises]] name = "quiz3" -path = "exercises/quiz3.rs" mode = "test" hint = """ To find the best solution to this challenge you're going to need to think back @@ -881,16 +879,16 @@ You may also need this: `use std::fmt::Display;`.""" [[exercises]] name = "lifetimes1" -path = "exercises/16_lifetimes/lifetimes1.rs" -mode = "compile" +dir = "16_lifetimes" +mode = "run" hint = """ Let the compiler guide you. Also take a look at the book if you need help: https://doc.rust-lang.org/book/ch10-03-lifetime-syntax.html""" [[exercises]] name = "lifetimes2" -path = "exercises/16_lifetimes/lifetimes2.rs" -mode = "compile" +dir = "16_lifetimes" +mode = "run" hint = """ Remember that the generic lifetime `'a` will get the concrete lifetime that is equal to the smaller of the lifetimes of `x` and `y`. @@ -903,8 +901,8 @@ inner block: [[exercises]] name = "lifetimes3" -path = "exercises/16_lifetimes/lifetimes3.rs" -mode = "compile" +dir = "16_lifetimes" +mode = "run" hint = """ If you use a lifetime annotation in a struct's fields, where else does it need to be added?""" @@ -913,7 +911,7 @@ to be added?""" [[exercises]] name = "tests1" -path = "exercises/17_tests/tests1.rs" +dir = "17_tests" mode = "test" hint = """ You don't even need to write any code to test -- you can just test values and @@ -928,7 +926,7 @@ ones pass, and which ones fail :)""" [[exercises]] name = "tests2" -path = "exercises/17_tests/tests2.rs" +dir = "17_tests" mode = "test" hint = """ Like the previous exercise, you don't need to write any code to get this test @@ -941,7 +939,7 @@ argument comes first and which comes second!""" [[exercises]] name = "tests3" -path = "exercises/17_tests/tests3.rs" +dir = "17_tests" mode = "test" hint = """ You can call a function right where you're passing arguments to `assert!`. So @@ -952,7 +950,7 @@ what you're doing using `!`, like `assert!(!having_fun())`.""" [[exercises]] name = "tests4" -path = "exercises/17_tests/tests4.rs" +dir = "17_tests" mode = "test" hint = """ We expect method `Rectangle::new()` to panic for negative values. @@ -966,7 +964,7 @@ https://doc.rust-lang.org/stable/book/ch11-01-writing-tests.html#checking-for-pa [[exercises]] name = "iterators1" -path = "exercises/18_iterators/iterators1.rs" +dir = "18_iterators" mode = "test" hint = """ Step 1: @@ -989,7 +987,7 @@ https://doc.rust-lang.org/std/iter/trait.Iterator.html for some ideas. [[exercises]] name = "iterators2" -path = "exercises/18_iterators/iterators2.rs" +dir = "18_iterators" mode = "test" hint = """ Step 1: @@ -1015,7 +1013,7 @@ powerful and very general. Rust just needs to know the desired type.""" [[exercises]] name = "iterators3" -path = "exercises/18_iterators/iterators3.rs" +dir = "18_iterators" mode = "test" hint = """ The `divide` function needs to return the correct error when even division is @@ -1034,7 +1032,7 @@ powerful! It can make the solution to this exercise infinitely easier.""" [[exercises]] name = "iterators4" -path = "exercises/18_iterators/iterators4.rs" +dir = "18_iterators" mode = "test" hint = """ In an imperative language, you might write a `for` loop that updates a mutable @@ -1046,7 +1044,7 @@ Hint 2: Check out the `fold` and `rfold` methods!""" [[exercises]] name = "iterators5" -path = "exercises/18_iterators/iterators5.rs" +dir = "18_iterators" mode = "test" hint = """ The documentation for the `std::iter::Iterator` trait contains numerous methods @@ -1065,7 +1063,7 @@ a different method that could make your code more compact than using `fold`.""" [[exercises]] name = "box1" -path = "exercises/19_smart_pointers/box1.rs" +dir = "19_smart_pointers" mode = "test" hint = """ Step 1: @@ -1089,7 +1087,7 @@ definition and try other types! [[exercises]] name = "rc1" -path = "exercises/19_smart_pointers/rc1.rs" +dir = "19_smart_pointers" mode = "test" hint = """ This is a straightforward exercise to use the `Rc` type. Each `Planet` has @@ -1108,8 +1106,8 @@ See more at: https://doc.rust-lang.org/book/ch15-04-rc.html [[exercises]] name = "arc1" -path = "exercises/19_smart_pointers/arc1.rs" -mode = "compile" +dir = "19_smart_pointers" +mode = "run" hint = """ Make `shared_numbers` be an `Arc` from the numbers vector. Then, in order to avoid creating a copy of `numbers`, you'll need to create `child_numbers` @@ -1126,7 +1124,7 @@ https://doc.rust-lang.org/stable/book/ch16-00-concurrency.html [[exercises]] name = "cow1" -path = "exercises/19_smart_pointers/cow1.rs" +dir = "19_smart_pointers" mode = "test" hint = """ If `Cow` already owns the data it doesn't need to clone it when `to_mut()` is @@ -1140,8 +1138,8 @@ on the `Cow` type. [[exercises]] name = "threads1" -path = "exercises/20_threads/threads1.rs" -mode = "compile" +dir = "20_threads" +mode = "run" hint = """ `JoinHandle` is a struct that is returned from a spawned thread: https://doc.rust-lang.org/std/thread/fn.spawn.html @@ -1158,8 +1156,8 @@ https://doc.rust-lang.org/std/thread/struct.JoinHandle.html [[exercises]] name = "threads2" -path = "exercises/20_threads/threads2.rs" -mode = "compile" +dir = "20_threads" +mode = "run" hint = """ `Arc` is an Atomic Reference Counted pointer that allows safe, shared access to **immutable** data. But we want to *change* the number of `jobs_completed` @@ -1180,7 +1178,7 @@ https://doc.rust-lang.org/book/ch16-03-shared-state.html#sharing-a-mutext-betwee [[exercises]] name = "threads3" -path = "exercises/20_threads/threads3.rs" +dir = "20_threads" mode = "test" hint = """ An alternate way to handle concurrency between threads is to use an `mpsc` @@ -1199,8 +1197,8 @@ See https://doc.rust-lang.org/book/ch16-02-message-passing.html for more info. [[exercises]] name = "macros1" -path = "exercises/21_macros/macros1.rs" -mode = "compile" +dir = "21_macros" +mode = "run" hint = """ When you call a macro, you need to add something special compared to a regular function call. If you're stuck, take a look at what's inside @@ -1208,8 +1206,8 @@ regular function call. If you're stuck, take a look at what's inside [[exercises]] name = "macros2" -path = "exercises/21_macros/macros2.rs" -mode = "compile" +dir = "21_macros" +mode = "run" hint = """ Macros don't quite play by the same rules as the rest of Rust, in terms of what's available where. @@ -1219,8 +1217,8 @@ Unlike other things in Rust, the order of "where you define a macro" versus [[exercises]] name = "macros3" -path = "exercises/21_macros/macros3.rs" -mode = "compile" +dir = "21_macros" +mode = "run" hint = """ In order to use a macro outside of its module, you need to do something special to the module to lift the macro out into its parent. @@ -1230,8 +1228,8 @@ exported macros, if you've seen any of those around.""" [[exercises]] name = "macros4" -path = "exercises/21_macros/macros4.rs" -mode = "compile" +dir = "21_macros" +mode = "run" hint = """ You only need to add a single character to make this compile. @@ -1247,7 +1245,7 @@ https://veykril.github.io/tlborm/""" [[exercises]] name = "clippy1" -path = "exercises/22_clippy/clippy1.rs" +dir = "22_clippy" mode = "clippy" hint = """ Rust stores the highest precision version of any long or infinite precision @@ -1263,14 +1261,14 @@ appropriate replacement constant from `std::f32::consts`...""" [[exercises]] name = "clippy2" -path = "exercises/22_clippy/clippy2.rs" +dir = "22_clippy" mode = "clippy" hint = """ `for` loops over `Option` values are more clearly expressed as an `if let`""" [[exercises]] name = "clippy3" -path = "exercises/22_clippy/clippy3.rs" +dir = "22_clippy" mode = "clippy" hint = "No hints this time!" @@ -1278,7 +1276,7 @@ hint = "No hints this time!" [[exercises]] name = "using_as" -path = "exercises/23_conversions/using_as.rs" +dir = "23_conversions" mode = "test" hint = """ Use the `as` operator to cast one of the operands in the last line of the @@ -1286,14 +1284,14 @@ Use the `as` operator to cast one of the operands in the last line of the [[exercises]] name = "from_into" -path = "exercises/23_conversions/from_into.rs" +dir = "23_conversions" mode = "test" hint = """ Follow the steps provided right before the `From` implementation""" [[exercises]] name = "from_str" -path = "exercises/23_conversions/from_str.rs" +dir = "23_conversions" mode = "test" hint = """ The implementation of `FromStr` should return an `Ok` with a `Person` object, @@ -1314,7 +1312,7 @@ https://doc.rust-lang.org/stable/rust-by-example/error/multiple_error_types/reen [[exercises]] name = "try_from_into" -path = "exercises/23_conversions/try_from_into.rs" +dir = "23_conversions" mode = "test" hint = """ Follow the steps provided right before the `TryFrom` implementation. @@ -1337,7 +1335,7 @@ Challenge: Can you make the `TryFrom` implementations generic over many integer [[exercises]] name = "as_ref_mut" -path = "exercises/23_conversions/as_ref_mut.rs" +dir = "23_conversions" mode = "test" hint = """ Add `AsRef` or `AsMut` as a trait bound to the functions.""" diff --git a/src/app_state.rs b/src/app_state.rs index 2ea3db4..1a051b9 100644 --- a/src/app_state.rs +++ b/src/app_state.rs @@ -4,52 +4,16 @@ use crossterm::{ terminal::{Clear, ClearType}, ExecutableCommand, }; -use serde::{Deserialize, Serialize}; -use std::{ - fs, - io::{StdoutLock, Write}, -}; - -use crate::{exercise::Exercise, FENISH_LINE}; - -const BAD_INDEX_ERR: &str = "The current exercise index is higher than the number of exercises"; - -#[derive(Serialize, Deserialize)] -#[serde(deny_unknown_fields)] -struct StateFile { - current_exercise_ind: usize, - progress: Vec, -} - -impl StateFile { - fn read(exercises: &[Exercise]) -> Option { - let file_content = fs::read(".rustlings-state.json").ok()?; +use std::io::{StdoutLock, Write}; - let slf: Self = serde_json::de::from_slice(&file_content).ok()?; +mod state_file; - if slf.progress.len() != exercises.len() || slf.current_exercise_ind >= exercises.len() { - return None; - } - - Some(slf) - } - - fn read_or_default(exercises: &[Exercise]) -> Self { - Self::read(exercises).unwrap_or_else(|| Self { - current_exercise_ind: 0, - progress: vec![false; exercises.len()], - }) - } +use crate::{exercise::Exercise, info_file::InfoFile, FENISH_LINE}; - fn write(&self) -> Result<()> { - let mut buf = Vec::with_capacity(1024); - serde_json::ser::to_writer(&mut buf, self).context("Failed to serialize the state")?; - fs::write(".rustlings-state.json", buf) - .context("Failed to write the state file `.rustlings-state.json`")?; +use self::state_file::{write, StateFileDeser}; - Ok(()) - } -} +const STATE_FILE_NAME: &str = ".rustlings-state.json"; +const BAD_INDEX_ERR: &str = "The current exercise index is higher than the number of exercises"; #[must_use] pub enum ExercisesProgress { @@ -58,52 +22,85 @@ pub enum ExercisesProgress { } pub struct AppState { - state_file: StateFile, - exercises: &'static [Exercise], + current_exercise_ind: usize, + exercises: Vec, n_done: u16, - current_exercise: &'static Exercise, - final_message: &'static str, + welcome_message: String, + final_message: String, } impl AppState { - pub fn new(mut exercises: Vec, mut final_message: String) -> Self { - // Leaking especially for sending the exercises to the debounce event handler. - // Leaking is not a problem because the `AppState` instance lives until - // the end of the program. - exercises.shrink_to_fit(); - let exercises = exercises.leak(); - final_message.shrink_to_fit(); - let final_message = final_message.leak(); - - let state_file = StateFile::read_or_default(exercises); - let n_done = state_file - .progress - .iter() - .fold(0, |acc, done| acc + u16::from(*done)); - let current_exercise = &exercises[state_file.current_exercise_ind]; + pub fn new(info_file: InfoFile) -> Self { + let mut exercises = info_file + .exercises + .into_iter() + .map(|mut exercise_info| { + // Leaking to be able to borrow in the watch mode `Table`. + // Leaking is not a problem because the `AppState` instance lives until + // the end of the program. + let path = Box::leak(exercise_info.path().into_boxed_path()); + + exercise_info.name.shrink_to_fit(); + let name = exercise_info.name.leak(); + + let hint = exercise_info.hint.trim().to_owned(); + + Exercise { + name, + path, + mode: exercise_info.mode, + hint, + done: false, + } + }) + .collect::>(); + + let (current_exercise_ind, n_done) = StateFileDeser::read().map_or((0, 0), |state_file| { + let mut state_file_exercises = + hashbrown::HashMap::with_capacity(state_file.exercises.len()); + + for (ind, exercise_state) in state_file.exercises.into_iter().enumerate() { + state_file_exercises.insert( + exercise_state.name, + (ind == state_file.current_exercise_ind, exercise_state.done), + ); + } + + let mut current_exercise_ind = 0; + let mut n_done = 0; + for (ind, exercise) in exercises.iter_mut().enumerate() { + if let Some((current, done)) = state_file_exercises.get(exercise.name) { + if *done { + exercise.done = true; + n_done += 1; + } + + if *current { + current_exercise_ind = ind; + } + } + } + + (current_exercise_ind, n_done) + }); Self { - state_file, + current_exercise_ind, exercises, n_done, - current_exercise, - final_message, + welcome_message: info_file.welcome_message.unwrap_or_default(), + final_message: info_file.final_message.unwrap_or_default(), } } #[inline] pub fn current_exercise_ind(&self) -> usize { - self.state_file.current_exercise_ind - } - - #[inline] - pub fn progress(&self) -> &[bool] { - &self.state_file.progress + self.current_exercise_ind } #[inline] - pub fn exercises(&self) -> &'static [Exercise] { - self.exercises + pub fn exercises(&self) -> &[Exercise] { + &self.exercises } #[inline] @@ -112,8 +109,8 @@ impl AppState { } #[inline] - pub fn current_exercise(&self) -> &'static Exercise { - self.current_exercise + pub fn current_exercise(&self) -> &Exercise { + &self.exercises[self.current_exercise_ind] } pub fn set_current_exercise_ind(&mut self, ind: usize) -> Result<()> { @@ -121,70 +118,61 @@ impl AppState { bail!(BAD_INDEX_ERR); } - self.state_file.current_exercise_ind = ind; - self.current_exercise = &self.exercises[ind]; + self.current_exercise_ind = ind; - self.state_file.write() + write(self) } pub fn set_current_exercise_by_name(&mut self, name: &str) -> Result<()> { - let (ind, exercise) = self + // O(N) is fine since this method is used only once until the program exits. + // Building a hashmap would have more overhead. + self.current_exercise_ind = self .exercises .iter() - .enumerate() - .find(|(_, exercise)| exercise.name == name) + .position(|exercise| exercise.name == name) .with_context(|| format!("No exercise found for '{name}'!"))?; - self.state_file.current_exercise_ind = ind; - self.current_exercise = exercise; - - self.state_file.write() + write(self) } pub fn set_pending(&mut self, ind: usize) -> Result<()> { - let done = self - .state_file - .progress - .get_mut(ind) - .context(BAD_INDEX_ERR)?; - - if *done { - *done = false; + let exercise = self.exercises.get_mut(ind).context(BAD_INDEX_ERR)?; + + if exercise.done { + exercise.done = false; self.n_done -= 1; - self.state_file.write()?; + write(self)?; } Ok(()) } fn next_pending_exercise_ind(&self) -> Option { - let current_ind = self.state_file.current_exercise_ind; - - if current_ind == self.state_file.progress.len() - 1 { + if self.current_exercise_ind == self.exercises.len() - 1 { // The last exercise is done. // Search for exercises not done from the start. - return self.state_file.progress[..current_ind] + return self.exercises[..self.current_exercise_ind] .iter() - .position(|done| !done); + .position(|exercise| !exercise.done); } // The done exercise isn't the last one. // Search for a pending exercise after the current one and then from the start. - match self.state_file.progress[current_ind + 1..] + match self.exercises[self.current_exercise_ind + 1..] .iter() - .position(|done| !done) + .position(|exercise| !exercise.done) { - Some(ind) => Some(current_ind + 1 + ind), - None => self.state_file.progress[..current_ind] + Some(ind) => Some(self.current_exercise_ind + 1 + ind), + None => self.exercises[..self.current_exercise_ind] .iter() - .position(|done| !done), + .position(|exercise| !exercise.done), } } pub fn done_current_exercise(&mut self, writer: &mut StdoutLock) -> Result { - let done = &mut self.state_file.progress[self.state_file.current_exercise_ind]; - if !*done { - *done = true; + let exercise = &mut self.exercises[self.current_exercise_ind]; + if !exercise.done { + exercise.done = true; self.n_done += 1; } @@ -198,15 +186,14 @@ impl AppState { if !exercise.run()?.status.success() { writer.write_fmt(format_args!("{}\n\n", "FAILED".red()))?; - self.state_file.current_exercise_ind = exercise_ind; - self.current_exercise = exercise; + self.current_exercise_ind = exercise_ind; // No check if the exercise is done before setting it to pending // because no pending exercise was found. - self.state_file.progress[exercise_ind] = false; + self.exercises[exercise_ind].done = false; self.n_done -= 1; - self.state_file.write()?; + write(self)?; return Ok(ExercisesProgress::Pending); } diff --git a/src/app_state/state_file.rs b/src/app_state/state_file.rs new file mode 100644 index 0000000..364a1fa --- /dev/null +++ b/src/app_state/state_file.rs @@ -0,0 +1,112 @@ +use anyhow::{Context, Result}; +use serde::{Deserialize, Serialize}; +use std::fs; + +use crate::exercise::Exercise; + +use super::{AppState, STATE_FILE_NAME}; + +#[derive(Deserialize)] +pub struct ExerciseStateDeser { + pub name: String, + pub done: bool, +} + +#[derive(Serialize)] +struct ExerciseStateSer<'a> { + name: &'a str, + done: bool, +} + +struct ExercisesStateSerializer<'a>(&'a [Exercise]); + +impl<'a> Serialize for ExercisesStateSerializer<'a> { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + let iter = self.0.iter().map(|exercise| ExerciseStateSer { + name: exercise.name, + done: exercise.done, + }); + + serializer.collect_seq(iter) + } +} + +#[derive(Deserialize)] +pub struct StateFileDeser { + pub current_exercise_ind: usize, + pub exercises: Vec, +} + +#[derive(Serialize)] +struct StateFileSer<'a> { + current_exercise_ind: usize, + exercises: ExercisesStateSerializer<'a>, +} + +impl StateFileDeser { + pub fn read() -> Option { + let file_content = fs::read(STATE_FILE_NAME).ok()?; + serde_json::de::from_slice(&file_content).ok() + } +} + +pub fn write(app_state: &AppState) -> Result<()> { + let content = StateFileSer { + current_exercise_ind: app_state.current_exercise_ind, + exercises: ExercisesStateSerializer(&app_state.exercises), + }; + + let mut buf = Vec::with_capacity(1024); + serde_json::ser::to_writer(&mut buf, &content).context("Failed to serialize the state")?; + fs::write(STATE_FILE_NAME, buf) + .with_context(|| format!("Failed to write the state file `{STATE_FILE_NAME}`"))?; + + Ok(()) +} + +#[cfg(test)] +mod tests { + use std::path::Path; + + use crate::info_file::Mode; + + use super::*; + + #[test] + fn ser_deser_sync() { + let current_exercise_ind = 1; + let exercises = [ + Exercise { + name: "1", + path: Path::new("exercises/1.rs"), + mode: Mode::Run, + hint: String::new(), + done: true, + }, + Exercise { + name: "2", + path: Path::new("exercises/2.rs"), + mode: Mode::Test, + hint: String::new(), + done: false, + }, + ]; + + let ser = StateFileSer { + current_exercise_ind, + exercises: ExercisesStateSerializer(&exercises), + }; + let deser: StateFileDeser = + serde_json::de::from_slice(&serde_json::ser::to_vec(&ser).unwrap()).unwrap(); + + assert_eq!(deser.current_exercise_ind, current_exercise_ind); + assert!(deser + .exercises + .iter() + .zip(exercises) + .all(|(deser, ser)| deser.name == ser.name && deser.done == ser.done)); + } +} diff --git a/src/exercise.rs b/src/exercise.rs index 6aa3b82..c5ece5f 100644 --- a/src/exercise.rs +++ b/src/exercise.rs @@ -1,66 +1,25 @@ use anyhow::{Context, Result}; -use serde::Deserialize; use std::{ - fmt::{self, Debug, Display, Formatter}, - fs::{self}, - path::PathBuf, + fmt::{self, Display, Formatter}, + path::Path, process::{Command, Output}, }; -use crate::embedded::{WriteStrategy, EMBEDDED_FILES}; - -// The mode of the exercise. -#[derive(Deserialize, Copy, Clone)] -#[serde(rename_all = "lowercase")] -pub enum Mode { - // The exercise should be compiled as a binary - Compile, - // The exercise should be compiled as a test harness - Test, - // The exercise should be linted with clippy - Clippy, -} - -#[derive(Deserialize)] -#[serde(deny_unknown_fields)] -pub struct InfoFile { - // TODO - pub welcome_message: Option, - pub final_message: Option, - pub exercises: Vec, -} - -impl InfoFile { - pub fn parse() -> Result { - // Read a local `info.toml` if it exists. - // Mainly to let the tests work for now. - let slf: Self = if let Ok(file_content) = fs::read_to_string("info.toml") { - toml_edit::de::from_str(&file_content) - } else { - toml_edit::de::from_str(include_str!("../info.toml")) - } - .context("Failed to parse `info.toml`")?; - - if slf.exercises.is_empty() { - panic!("{NO_EXERCISES_ERR}"); - } - - Ok(slf) - } -} +use crate::{ + embedded::{WriteStrategy, EMBEDDED_FILES}, + info_file::Mode, +}; -// Deserialized from the `info.toml` file. -#[derive(Deserialize)] -#[serde(deny_unknown_fields)] pub struct Exercise { - // Name of the exercise - pub name: String, - // The path to the file containing the exercise's source code - pub path: PathBuf, + // Exercise's unique name + pub name: &'static str, + // Exercise's path + pub path: &'static Path, // The mode of the exercise pub mode: Mode, // The hint text associated with the exercise pub hint: String, + pub done: bool, } impl Exercise { @@ -79,7 +38,7 @@ impl Exercise { .arg("always") .arg("-q") .arg("--bin") - .arg(&self.name) + .arg(self.name) .args(args) .output() .context("Failed to run Cargo") @@ -87,7 +46,7 @@ impl Exercise { pub fn run(&self) -> Result { match self.mode { - Mode::Compile => self.cargo_cmd("run", &[]), + Mode::Run => self.cargo_cmd("run", &[]), Mode::Test => self.cargo_cmd("test", &["--", "--nocapture", "--format", "pretty"]), Mode::Clippy => self.cargo_cmd( "clippy", @@ -98,7 +57,7 @@ impl Exercise { pub fn reset(&self) -> Result<()> { EMBEDDED_FILES - .write_exercise_to_disk(&self.path, WriteStrategy::Overwrite) + .write_exercise_to_disk(self.path, WriteStrategy::Overwrite) .with_context(|| format!("Failed to reset the exercise {self}")) } } @@ -108,6 +67,3 @@ impl Display for Exercise { Display::fmt(&self.path.display(), f) } } - -const NO_EXERCISES_ERR: &str = "There are no exercises yet! -If you are developing third-party exercises, add at least one exercise before testing."; diff --git a/src/info_file.rs b/src/info_file.rs new file mode 100644 index 0000000..dc97b92 --- /dev/null +++ b/src/info_file.rs @@ -0,0 +1,81 @@ +use anyhow::{bail, Context, Error, Result}; +use serde::Deserialize; +use std::{fs, path::PathBuf}; + +// The mode of the exercise. +#[derive(Deserialize, Copy, Clone)] +#[serde(rename_all = "lowercase")] +pub enum Mode { + // The exercise should be compiled as a binary + Run, + // The exercise should be compiled as a test harness + Test, + // The exercise should be linted with clippy + Clippy, +} + +// Deserialized from the `info.toml` file. +#[derive(Deserialize)] +pub struct ExerciseInfo { + // Name of the exercise + pub name: String, + // The exercise's directory inside the `exercises` directory + pub dir: Option, + // The mode of the exercise + pub mode: Mode, + // The hint text associated with the exercise + pub hint: String, +} + +impl ExerciseInfo { + pub fn path(&self) -> PathBuf { + let path = if let Some(dir) = &self.dir { + format!("exercises/{dir}/{}.rs", self.name) + } else { + format!("exercises/{}.rs", self.name) + }; + + PathBuf::from(path) + } +} + +#[derive(Deserialize)] +pub struct InfoFile { + pub welcome_message: Option, + pub final_message: Option, + pub exercises: Vec, +} + +impl InfoFile { + pub fn parse() -> Result { + // Read a local `info.toml` if it exists. + let slf: Self = match fs::read_to_string("info.toml") { + Ok(file_content) => toml_edit::de::from_str(&file_content) + .context("Failed to parse the `info.toml` file")?, + Err(e) => match e.kind() { + std::io::ErrorKind::NotFound => { + toml_edit::de::from_str(include_str!("../info.toml")) + .context("Failed to parse the embedded `info.toml` file")? + } + _ => return Err(Error::from(e).context("Failed to read the `info.toml` file")), + }, + }; + + if slf.exercises.is_empty() { + bail!("{NO_EXERCISES_ERR}"); + } + + let mut names_set = hashbrown::HashSet::with_capacity(slf.exercises.len()); + for exercise in &slf.exercises { + if !names_set.insert(exercise.name.as_str()) { + bail!("Exercise names must all be unique!") + } + } + drop(names_set); + + Ok(slf) + } +} + +const NO_EXERCISES_ERR: &str = "There are no exercises yet! +If you are developing third-party exercises, add at least one exercise before testing."; diff --git a/src/init.rs b/src/init.rs index 093610a..2badf37 100644 --- a/src/init.rs +++ b/src/init.rs @@ -6,17 +6,21 @@ use std::{ path::Path, }; -use crate::{embedded::EMBEDDED_FILES, exercise::Exercise}; +use crate::{embedded::EMBEDDED_FILES, info_file::ExerciseInfo}; -fn create_cargo_toml(exercises: &[Exercise]) -> io::Result<()> { +fn create_cargo_toml(exercise_infos: &[ExerciseInfo]) -> io::Result<()> { let mut cargo_toml = Vec::with_capacity(1 << 13); cargo_toml.extend_from_slice(b"bin = [\n"); - for exercise in exercises { + for exercise_info in exercise_infos { cargo_toml.extend_from_slice(b" { name = \""); - cargo_toml.extend_from_slice(exercise.name.as_bytes()); - cargo_toml.extend_from_slice(b"\", path = \""); - cargo_toml.extend_from_slice(exercise.path.to_str().unwrap().as_bytes()); - cargo_toml.extend_from_slice(b"\" },\n"); + cargo_toml.extend_from_slice(exercise_info.name.as_bytes()); + cargo_toml.extend_from_slice(b"\", path = \"exercises/"); + if let Some(dir) = &exercise_info.dir { + cargo_toml.extend_from_slice(dir.as_bytes()); + cargo_toml.extend_from_slice(b"/"); + } + cargo_toml.extend_from_slice(exercise_info.name.as_bytes()); + cargo_toml.extend_from_slice(b".rs\" },\n"); } cargo_toml.extend_from_slice( @@ -54,7 +58,7 @@ fn create_vscode_dir() -> Result<()> { Ok(()) } -pub fn init(exercises: &[Exercise]) -> Result<()> { +pub fn init(exercise_infos: &[ExerciseInfo]) -> Result<()> { if Path::new("exercises").is_dir() && Path::new("Cargo.toml").is_file() { bail!(PROBABLY_IN_RUSTLINGS_DIR_ERR); } @@ -74,7 +78,8 @@ pub fn init(exercises: &[Exercise]) -> Result<()> { .init_exercises_dir() .context("Failed to initialize the `rustlings/exercises` directory")?; - create_cargo_toml(exercises).context("Failed to create the file `rustlings/Cargo.toml`")?; + create_cargo_toml(exercise_infos) + .context("Failed to create the file `rustlings/Cargo.toml`")?; create_gitignore().context("Failed to create the file `rustlings/.gitignore`")?; diff --git a/src/list.rs b/src/list.rs index de120ea..2bb813d 100644 --- a/src/list.rs +++ b/src/list.rs @@ -5,7 +5,7 @@ use crossterm::{ ExecutableCommand, }; use ratatui::{backend::CrosstermBackend, Terminal}; -use std::{fmt::Write, io}; +use std::io; mod state; @@ -72,14 +72,7 @@ pub fn list(app_state: &mut AppState) -> Result<()> { ui_state.message.push_str(message); } KeyCode::Char('r') => { - let Some(exercise) = ui_state.reset_selected()? else { - continue; - }; - - ui_state = ui_state.with_updated_rows(); - ui_state - .message - .write_fmt(format_args!("The exercise {exercise} has been reset!"))?; + ui_state = ui_state.with_reset_selected()?; } KeyCode::Char('c') => { ui_state.selected_to_current_exercise()?; diff --git a/src/list/state.rs b/src/list/state.rs index 0dcfe88..38391a4 100644 --- a/src/list/state.rs +++ b/src/list/state.rs @@ -6,8 +6,9 @@ use ratatui::{ widgets::{Block, Borders, HighlightSpacing, Paragraph, Row, Table, TableState}, Frame, }; +use std::fmt::Write; -use crate::{app_state::AppState, exercise::Exercise, progress_bar::progress_bar_ratatui}; +use crate::{app_state::AppState, progress_bar::progress_bar_ratatui}; #[derive(Copy, Clone, PartialEq, Eq)] pub enum Filter { @@ -34,10 +35,9 @@ impl<'a> UiState<'a> { .app_state .exercises() .iter() - .zip(self.app_state.progress().iter().copied()) .enumerate() - .filter_map(|(ind, (exercise, done))| { - let exercise_state = if done { + .filter_map(|(ind, exercise)| { + let exercise_state = if exercise.done { if self.filter == Filter::Pending { return None; } @@ -62,7 +62,7 @@ impl<'a> UiState<'a> { Some(Row::new([ next, exercise_state, - Span::raw(&exercise.name), + Span::raw(exercise.name), Span::raw(exercise.path.to_string_lossy()), ])) }); @@ -212,29 +212,30 @@ impl<'a> UiState<'a> { Ok(()) } - pub fn reset_selected(&mut self) -> Result> { + pub fn with_reset_selected(mut self) -> Result { let Some(selected) = self.table_state.selected() else { - return Ok(None); + return Ok(self); }; let (ind, exercise) = self .app_state .exercises() .iter() - .zip(self.app_state.progress()) .enumerate() - .filter_map(|(ind, (exercise, done))| match self.filter { - Filter::Done => done.then_some((ind, exercise)), - Filter::Pending => (!done).then_some((ind, exercise)), + .filter_map(|(ind, exercise)| match self.filter { + Filter::Done => exercise.done.then_some((ind, exercise)), + Filter::Pending => (!exercise.done).then_some((ind, exercise)), Filter::None => Some((ind, exercise)), }) .nth(selected) .context("Invalid selection index")?; - self.app_state.set_pending(ind)?; exercise.reset()?; + self.message + .write_fmt(format_args!("The exercise {exercise} has been reset!"))?; + self.app_state.set_pending(ind)?; - Ok(Some(exercise)) + Ok(self.with_updated_rows()) } pub fn selected_to_current_exercise(&mut self) -> Result<()> { @@ -244,12 +245,12 @@ impl<'a> UiState<'a> { let ind = self .app_state - .progress() + .exercises() .iter() .enumerate() - .filter_map(|(ind, done)| match self.filter { - Filter::Done => done.then_some(ind), - Filter::Pending => (!done).then_some(ind), + .filter_map(|(ind, exercise)| match self.filter { + Filter::Done => exercise.done.then_some(ind), + Filter::Pending => (!exercise.done).then_some(ind), Filter::None => Some(ind), }) .nth(selected) diff --git a/src/main.rs b/src/main.rs index cdfa21f..a96e323 100644 --- a/src/main.rs +++ b/src/main.rs @@ -5,6 +5,7 @@ use std::{path::Path, process::exit}; mod app_state; mod embedded; mod exercise; +mod info_file; mod init; mod list; mod progress_bar; @@ -13,7 +14,7 @@ mod watch; use self::{ app_state::AppState, - exercise::InfoFile, + info_file::InfoFile, init::init, list::list, run::run, @@ -54,12 +55,10 @@ fn main() -> Result<()> { which::which("cargo").context(CARGO_NOT_FOUND_ERR)?; - let mut info_file = InfoFile::parse()?; - info_file.exercises.shrink_to_fit(); - let exercises = info_file.exercises; + let info_file = InfoFile::parse()?; if matches!(args.command, Some(Subcommands::Init)) { - init(&exercises).context("Initialization failed")?; + init(&info_file.exercises).context("Initialization failed")?; println!("{POST_INIT_MSG}"); return Ok(()); @@ -68,18 +67,29 @@ fn main() -> Result<()> { exit(1); } - let mut app_state = AppState::new(exercises, info_file.final_message.unwrap_or_default()); + let mut app_state = AppState::new(info_file); match args.command { - None => loop { - match watch(&mut app_state)? { - WatchExit::Shutdown => break, - // It is much easier to exit the watch mode, launch the list mode and then restart - // the watch mode instead of trying to pause the watch threads and correct the - // watch state. - WatchExit::List => list(&mut app_state)?, + None => { + // For the the notify event handler thread. + // Leaking is not a problem because the slice lives until the end of the program. + let exercise_paths = app_state + .exercises() + .iter() + .map(|exercise| exercise.path) + .collect::>() + .leak(); + + loop { + match watch(&mut app_state, exercise_paths)? { + WatchExit::Shutdown => break, + // It is much easier to exit the watch mode, launch the list mode and then restart + // the watch mode instead of trying to pause the watch threads and correct the + // watch state. + WatchExit::List => list(&mut app_state)?, + } } - }, + } // `Init` is handled above. Some(Subcommands::Init) => (), Some(Subcommands::Run { name }) => { @@ -90,10 +100,10 @@ fn main() -> Result<()> { } Some(Subcommands::Reset { name }) => { app_state.set_current_exercise_by_name(&name)?; - app_state.set_pending(app_state.current_exercise_ind())?; let exercise = app_state.current_exercise(); exercise.reset()?; println!("The exercise {exercise} has been reset!"); + app_state.set_pending(app_state.current_exercise_ind())?; } Some(Subcommands::Hint { name }) => { app_state.set_current_exercise_by_name(&name)?; diff --git a/src/run.rs b/src/run.rs index 4748549..9c504b5 100644 --- a/src/run.rs +++ b/src/run.rs @@ -17,7 +17,7 @@ pub fn run(app_state: &mut AppState) -> Result<()> { if !output.status.success() { app_state.set_pending(app_state.current_exercise_ind())?; - bail!("Ran {exercise} with errors"); + bail!("Ran {} with errors", app_state.current_exercise()); } stdout.write_fmt(format_args!( diff --git a/src/watch.rs b/src/watch.rs index beb69b3..58e829f 100644 --- a/src/watch.rs +++ b/src/watch.rs @@ -11,14 +11,14 @@ use std::{ time::Duration, }; -mod debounce_event; +mod notify_event; mod state; mod terminal_event; use crate::app_state::{AppState, ExercisesProgress}; use self::{ - debounce_event::DebounceEventHandler, + notify_event::DebounceEventHandler, state::WatchState, terminal_event::{terminal_event_handler, InputEvent}, }; @@ -40,13 +40,16 @@ pub enum WatchExit { List, } -pub fn watch(app_state: &mut AppState) -> Result { +pub fn watch( + app_state: &mut AppState, + exercise_paths: &'static [&'static Path], +) -> Result { let (tx, rx) = channel(); let mut debouncer = new_debouncer( Duration::from_secs(1), DebounceEventHandler { tx: tx.clone(), - exercises: app_state.exercises(), + exercise_paths, }, )?; debouncer @@ -85,10 +88,10 @@ pub fn watch(app_state: &mut AppState) -> Result { watch_state.render()?; } WatchEvent::NotifyErr(e) => { - return Err(Error::from(e).context("Exercise file watcher failed")) + return Err(Error::from(e).context("Exercise file watcher failed")); } WatchEvent::TerminalEventErr(e) => { - return Err(Error::from(e).context("Terminal event listener failed")) + return Err(Error::from(e).context("Terminal event listener failed")); } } } diff --git a/src/watch/debounce_event.rs b/src/watch/debounce_event.rs deleted file mode 100644 index 1dc92cb..0000000 --- a/src/watch/debounce_event.rs +++ /dev/null @@ -1,44 +0,0 @@ -use notify_debouncer_mini::{DebounceEventResult, DebouncedEventKind}; -use std::sync::mpsc::Sender; - -use crate::exercise::Exercise; - -use super::WatchEvent; - -pub struct DebounceEventHandler { - pub tx: Sender, - pub exercises: &'static [Exercise], -} - -impl notify_debouncer_mini::DebounceEventHandler for DebounceEventHandler { - fn handle_event(&mut self, event: DebounceEventResult) { - let event = match event { - Ok(event) => { - let Some(exercise_ind) = event - .iter() - .filter_map(|event| { - if event.kind != DebouncedEventKind::Any - || !event.path.extension().is_some_and(|ext| ext == "rs") - { - return None; - } - - self.exercises - .iter() - .position(|exercise| event.path.ends_with(&exercise.path)) - }) - .min() - else { - return; - }; - - WatchEvent::FileChange { exercise_ind } - } - Err(e) => WatchEvent::NotifyErr(e), - }; - - // An error occurs when the receiver is dropped. - // After dropping the receiver, the debouncer guard should also be dropped. - let _ = self.tx.send(event); - } -} diff --git a/src/watch/notify_event.rs b/src/watch/notify_event.rs new file mode 100644 index 0000000..0c8d669 --- /dev/null +++ b/src/watch/notify_event.rs @@ -0,0 +1,42 @@ +use notify_debouncer_mini::{DebounceEventResult, DebouncedEventKind}; +use std::{path::Path, sync::mpsc::Sender}; + +use super::WatchEvent; + +pub struct DebounceEventHandler { + pub tx: Sender, + pub exercise_paths: &'static [&'static Path], +} + +impl notify_debouncer_mini::DebounceEventHandler for DebounceEventHandler { + fn handle_event(&mut self, event: DebounceEventResult) { + let event = match event { + Ok(event) => { + let Some(exercise_ind) = event + .iter() + .filter_map(|event| { + if event.kind != DebouncedEventKind::Any + || !event.path.extension().is_some_and(|ext| ext == "rs") + { + return None; + } + + self.exercise_paths + .iter() + .position(|path| event.path.ends_with(path)) + }) + .min() + else { + return; + }; + + WatchEvent::FileChange { exercise_ind } + } + Err(e) => WatchEvent::NotifyErr(e), + }; + + // An error occurs when the receiver is dropped. + // After dropping the receiver, the debouncer guard should also be dropped. + let _ = self.tx.send(event); + } +} -- cgit v1.2.3 From 9dcc4b7df5f539b10117e97870a9f1cb01ca040d Mon Sep 17 00:00:00 2001 From: mo8it Date: Sun, 14 Apr 2024 05:13:27 +0200 Subject: Simplify the state file --- .gitignore | 2 +- Cargo.lock | 12 ----- Cargo.toml | 1 - src/app_state.rs | 128 +++++++++++++++++++++++++++++--------------- src/app_state/state_file.rs | 110 ------------------------------------- src/init.rs | 2 +- 6 files changed, 86 insertions(+), 169 deletions(-) delete mode 100644 src/app_state/state_file.rs (limited to 'Cargo.lock') diff --git a/.gitignore b/.gitignore index c9172e0..80f9092 100644 --- a/.gitignore +++ b/.gitignore @@ -4,7 +4,7 @@ target/ /dev/Cargo.lock # State file -.rustlings-state.json +.rustlings-state.txt # oranda public/ diff --git a/Cargo.lock b/Cargo.lock index dbf1923..6bc68f0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -690,7 +690,6 @@ dependencies = [ "ratatui", "rustlings-macros", "serde", - "serde_json", "toml_edit", "which", ] @@ -749,17 +748,6 @@ dependencies = [ "syn 2.0.58", ] -[[package]] -name = "serde_json" -version = "1.0.115" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12dc5c46daa8e9fdf4f5e71b6cf9a53f2487da0e86e55808e2d35539666497dd" -dependencies = [ - "itoa", - "ryu", - "serde", -] - [[package]] name = "serde_spanned" version = "0.6.5" diff --git a/Cargo.toml b/Cargo.toml index 14ae9a1..07865ab 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -41,7 +41,6 @@ hashbrown = "0.14.3" notify-debouncer-mini = "0.4.1" ratatui = "0.26.1" rustlings-macros = { path = "rustlings-macros" } -serde_json = "1.0.115" serde.workspace = true toml_edit.workspace = true which = "6.0.1" diff --git a/src/app_state.rs b/src/app_state.rs index 98c6384..9a378de 100644 --- a/src/app_state.rs +++ b/src/app_state.rs @@ -4,15 +4,14 @@ use crossterm::{ terminal::{Clear, ClearType}, ExecutableCommand, }; -use std::io::{StdoutLock, Write}; - -mod state_file; +use std::{ + fs::{self, File}, + io::{Read, StdoutLock, Write}, +}; use crate::{exercise::Exercise, info_file::InfoFile, FENISH_LINE}; -use self::state_file::{write, StateFileDeser}; - -const STATE_FILE_NAME: &str = ".rustlings-state.json"; +const STATE_FILE_NAME: &str = ".rustlings-state.txt"; const BAD_INDEX_ERR: &str = "The current exercise index is higher than the number of exercises"; #[must_use] @@ -27,11 +26,51 @@ pub struct AppState { n_done: u16, welcome_message: String, final_message: String, + file_buf: Vec, } impl AppState { + fn update_from_file(&mut self) { + self.file_buf.clear(); + self.n_done = 0; + + if File::open(STATE_FILE_NAME) + .and_then(|mut file| file.read_to_end(&mut self.file_buf)) + .is_ok() + { + let mut lines = self.file_buf.split(|c| *c == b'\n'); + let Some(current_exercise_name) = lines.next() else { + return; + }; + + if lines.next().is_none() { + return; + } + + let mut done_exercises = hashbrown::HashSet::with_capacity(self.exercises.len()); + + for done_exerise_name in lines { + if done_exerise_name.is_empty() { + break; + } + done_exercises.insert(done_exerise_name); + } + + for (ind, exercise) in self.exercises.iter_mut().enumerate() { + if done_exercises.contains(exercise.name.as_bytes()) { + exercise.done = true; + self.n_done += 1; + } + + if exercise.name.as_bytes() == current_exercise_name { + self.current_exercise_ind = ind; + } + } + } + } + pub fn new(info_file: InfoFile) -> Self { - let mut exercises = info_file + let exercises = info_file .exercises .into_iter() .map(|mut exercise_info| { @@ -55,42 +94,18 @@ impl AppState { }) .collect::>(); - let (current_exercise_ind, n_done) = StateFileDeser::read().map_or((0, 0), |state_file| { - let mut state_file_exercises = - hashbrown::HashMap::with_capacity(state_file.exercises.len()); - - for (ind, exercise_state) in state_file.exercises.into_iter().enumerate() { - state_file_exercises.insert( - exercise_state.name, - (ind == state_file.current_exercise_ind, exercise_state.done), - ); - } - - let mut current_exercise_ind = 0; - let mut n_done = 0; - for (ind, exercise) in exercises.iter_mut().enumerate() { - if let Some((current, done)) = state_file_exercises.get(exercise.name) { - if *done { - exercise.done = true; - n_done += 1; - } - - if *current { - current_exercise_ind = ind; - } - } - } - - (current_exercise_ind, n_done) - }); - - Self { - current_exercise_ind, + let mut slf = Self { + current_exercise_ind: 0, exercises, - n_done, + n_done: 0, welcome_message: info_file.welcome_message.unwrap_or_default(), final_message: info_file.final_message.unwrap_or_default(), - } + file_buf: Vec::with_capacity(2048), + }; + + slf.update_from_file(); + + slf } #[inline] @@ -120,7 +135,7 @@ impl AppState { self.current_exercise_ind = ind; - write(self) + self.write() } pub fn set_current_exercise_by_name(&mut self, name: &str) -> Result<()> { @@ -132,7 +147,7 @@ impl AppState { .position(|exercise| exercise.name == name) .with_context(|| format!("No exercise found for '{name}'!"))?; - write(self) + self.write() } pub fn set_pending(&mut self, ind: usize) -> Result<()> { @@ -141,7 +156,7 @@ impl AppState { if exercise.done { exercise.done = false; self.n_done -= 1; - write(self)?; + self.write()?; } Ok(()) @@ -193,7 +208,7 @@ impl AppState { self.exercises[exercise_ind].done = false; self.n_done -= 1; - write(self)?; + self.write()?; return Ok(ExercisesProgress::Pending); } @@ -213,6 +228,31 @@ impl AppState { Ok(ExercisesProgress::Pending) } + + // Write the state file. + // The file's format is very simple: + // - The first line is the name of the current exercise. + // - The second line is an empty line. + // - All remaining lines are the names of done exercises. + fn write(&mut self) -> Result<()> { + self.file_buf.clear(); + + self.file_buf + .extend_from_slice(self.current_exercise().name.as_bytes()); + self.file_buf.extend_from_slice(b"\n\n"); + + for exercise in &self.exercises { + if exercise.done { + self.file_buf.extend_from_slice(exercise.name.as_bytes()); + self.file_buf.extend_from_slice(b"\n"); + } + } + + fs::write(STATE_FILE_NAME, &self.file_buf) + .with_context(|| format!("Failed to write the state file {STATE_FILE_NAME}"))?; + + Ok(()) + } } const RERUNNING_ALL_EXERCISES_MSG: &[u8] = b" diff --git a/src/app_state/state_file.rs b/src/app_state/state_file.rs deleted file mode 100644 index 4e4a0e1..0000000 --- a/src/app_state/state_file.rs +++ /dev/null @@ -1,110 +0,0 @@ -use anyhow::{Context, Result}; -use serde::{Deserialize, Serialize}; -use std::fs; - -use crate::exercise::Exercise; - -use super::{AppState, STATE_FILE_NAME}; - -#[derive(Deserialize)] -pub struct ExerciseStateDeser { - pub name: String, - pub done: bool, -} - -#[derive(Serialize)] -struct ExerciseStateSer<'a> { - name: &'a str, - done: bool, -} - -struct ExercisesStateSerializer<'a>(&'a [Exercise]); - -impl<'a> Serialize for ExercisesStateSerializer<'a> { - fn serialize(&self, serializer: S) -> Result - where - S: serde::Serializer, - { - let iter = self.0.iter().map(|exercise| ExerciseStateSer { - name: exercise.name, - done: exercise.done, - }); - - serializer.collect_seq(iter) - } -} - -#[derive(Deserialize)] -pub struct StateFileDeser { - pub current_exercise_ind: usize, - pub exercises: Vec, -} - -#[derive(Serialize)] -struct StateFileSer<'a> { - current_exercise_ind: usize, - exercises: ExercisesStateSerializer<'a>, -} - -impl StateFileDeser { - pub fn read() -> Option { - let file_content = fs::read(STATE_FILE_NAME).ok()?; - serde_json::de::from_slice(&file_content).ok() - } -} - -pub fn write(app_state: &AppState) -> Result<()> { - let content = StateFileSer { - current_exercise_ind: app_state.current_exercise_ind, - exercises: ExercisesStateSerializer(&app_state.exercises), - }; - - let mut buf = Vec::with_capacity(4096); - serde_json::ser::to_writer(&mut buf, &content).context("Failed to serialize the state")?; - fs::write(STATE_FILE_NAME, buf) - .with_context(|| format!("Failed to write the state file `{STATE_FILE_NAME}`"))?; - - Ok(()) -} - -#[cfg(test)] -mod tests { - use crate::info_file::Mode; - - use super::*; - - #[test] - fn ser_deser_sync() { - let current_exercise_ind = 1; - let exercises = [ - Exercise { - name: "1", - path: "exercises/1.rs", - mode: Mode::Run, - hint: String::new(), - done: true, - }, - Exercise { - name: "2", - path: "exercises/2.rs", - mode: Mode::Test, - hint: String::new(), - done: false, - }, - ]; - - let ser = StateFileSer { - current_exercise_ind, - exercises: ExercisesStateSerializer(&exercises), - }; - let deser: StateFileDeser = - serde_json::de::from_slice(&serde_json::ser::to_vec(&ser).unwrap()).unwrap(); - - assert_eq!(deser.current_exercise_ind, current_exercise_ind); - assert!(deser - .exercises - .iter() - .zip(exercises) - .all(|(deser, ser)| deser.name == ser.name && deser.done == ser.done)); - } -} diff --git a/src/init.rs b/src/init.rs index 2badf37..4ee503a 100644 --- a/src/init.rs +++ b/src/init.rs @@ -89,7 +89,7 @@ pub fn init(exercise_infos: &[ExerciseInfo]) -> Result<()> { } const GITIGNORE: &[u8] = b"/target -/.rustlings-state.json +/.rustlings-state.txt "; const VS_CODE_EXTENSIONS_JSON: &[u8] = br#"{"recommendations":["rust-lang.rust-analyzer"]}"#; -- cgit v1.2.3 From 1c90575b9fe0f0fb32006e000aefff10d8a4a39c Mon Sep 17 00:00:00 2001 From: mo8it Date: Sun, 14 Apr 2024 05:13:50 +0200 Subject: Update deps --- Cargo.lock | 59 +++++++++++++++++++++++++++++++++-------------------------- 1 file changed, 33 insertions(+), 26 deletions(-) (limited to 'Cargo.lock') diff --git a/Cargo.lock b/Cargo.lock index 6bc68f0..5cfebe6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -261,9 +261,9 @@ checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10" [[package]] name = "either" -version = "1.10.0" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11157ac094ffbdde99aa67b23417ebdd801842852b500e395a45a9c0aac03e4a" +checksum = "a47c1c47d2f5964e29c61246e81db715514cd532db6b5116a25ea3c03d6780a2" [[package]] name = "equivalent" @@ -1000,7 +1000,7 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets 0.52.4", + "windows-targets 0.52.5", ] [[package]] @@ -1020,17 +1020,18 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.52.4" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7dd37b7e5ab9018759f893a1952c9420d060016fc19a472b4bb20d1bdd694d1b" +checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" dependencies = [ - "windows_aarch64_gnullvm 0.52.4", - "windows_aarch64_msvc 0.52.4", - "windows_i686_gnu 0.52.4", - "windows_i686_msvc 0.52.4", - "windows_x86_64_gnu 0.52.4", - "windows_x86_64_gnullvm 0.52.4", - "windows_x86_64_msvc 0.52.4", + "windows_aarch64_gnullvm 0.52.5", + "windows_aarch64_msvc 0.52.5", + "windows_i686_gnu 0.52.5", + "windows_i686_gnullvm", + "windows_i686_msvc 0.52.5", + "windows_x86_64_gnu 0.52.5", + "windows_x86_64_gnullvm 0.52.5", + "windows_x86_64_msvc 0.52.5", ] [[package]] @@ -1041,9 +1042,9 @@ checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" [[package]] name = "windows_aarch64_gnullvm" -version = "0.52.4" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bcf46cf4c365c6f2d1cc93ce535f2c8b244591df96ceee75d8e83deb70a9cac9" +checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" [[package]] name = "windows_aarch64_msvc" @@ -1053,9 +1054,9 @@ checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" [[package]] name = "windows_aarch64_msvc" -version = "0.52.4" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da9f259dd3bcf6990b55bffd094c4f7235817ba4ceebde8e6d11cd0c5633b675" +checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" [[package]] name = "windows_i686_gnu" @@ -1065,9 +1066,15 @@ checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" [[package]] name = "windows_i686_gnu" -version = "0.52.4" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b474d8268f99e0995f25b9f095bc7434632601028cf86590aea5c8a5cb7801d3" +checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" [[package]] name = "windows_i686_msvc" @@ -1077,9 +1084,9 @@ checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" [[package]] name = "windows_i686_msvc" -version = "0.52.4" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1515e9a29e5bed743cb4415a9ecf5dfca648ce85ee42e15873c3cd8610ff8e02" +checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" [[package]] name = "windows_x86_64_gnu" @@ -1089,9 +1096,9 @@ checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" [[package]] name = "windows_x86_64_gnu" -version = "0.52.4" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5eee091590e89cc02ad514ffe3ead9eb6b660aedca2183455434b93546371a03" +checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" [[package]] name = "windows_x86_64_gnullvm" @@ -1101,9 +1108,9 @@ checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" [[package]] name = "windows_x86_64_gnullvm" -version = "0.52.4" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77ca79f2451b49fa9e2af39f0747fe999fcda4f5e241b2898624dca97a1f2177" +checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" [[package]] name = "windows_x86_64_msvc" @@ -1113,9 +1120,9 @@ checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" [[package]] name = "windows_x86_64_msvc" -version = "0.52.4" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32b752e52a2da0ddfbdbcc6fceadfeede4c939ed16d13e648833a61dfb611ed8" +checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" [[package]] name = "winnow" -- cgit v1.2.3 From 15ca847c37c170590abe6caa53dba5606d956341 Mon Sep 17 00:00:00 2001 From: mo8it Date: Mon, 15 Apr 2024 02:11:27 +0200 Subject: Implement third-party exercises trust handling --- Cargo.lock | 80 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Cargo.toml | 1 + src/init.rs | 4 ++- src/main.rs | 53 +++++++++++++++++++++++++++++++--------- src/trust.rs | 72 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 198 insertions(+), 12 deletions(-) create mode 100644 src/trust.rs (limited to 'Cargo.lock') diff --git a/Cargo.lock b/Cargo.lock index 5cfebe6..671a03f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -253,6 +253,27 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6184e33543162437515c2e2b48714794e37845ec9851711914eec9d308f6ebe8" +[[package]] +name = "dirs" +version = "5.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44c45a9d03d6676652bcb5e724c7e988de1acad23a711b5217ab9cbecbec2225" +dependencies = [ + "dirs-sys", +] + +[[package]] +name = "dirs-sys" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c" +dependencies = [ + "libc", + "option-ext", + "redox_users", + "windows-sys 0.48.0", +] + [[package]] name = "doc-comment" version = "0.3.3" @@ -320,6 +341,17 @@ dependencies = [ "toml_edit", ] +[[package]] +name = "getrandom" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94b22e06ecb0110981051723910cbf0b5f5e09a2062dd7663334ee79a9d1286c" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + [[package]] name = "hashbrown" version = "0.14.3" @@ -428,6 +460,16 @@ version = "0.2.153" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" +[[package]] +name = "libredox" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" +dependencies = [ + "bitflags 2.5.0", + "libc", +] + [[package]] name = "linux-raw-sys" version = "0.4.13" @@ -528,6 +570,12 @@ version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" +[[package]] +name = "option-ext" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" + [[package]] name = "parking_lot" version = "0.12.1" @@ -634,6 +682,17 @@ dependencies = [ "bitflags 1.3.2", ] +[[package]] +name = "redox_users" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd283d9651eeda4b2a83a43c1c91b266c40fd76ecd39a50a8c630ae69dc72891" +dependencies = [ + "getrandom", + "libredox", + "thiserror", +] + [[package]] name = "regex" version = "1.10.4" @@ -684,6 +743,7 @@ dependencies = [ "assert_cmd", "clap", "crossterm", + "dirs", "hashbrown", "notify-debouncer-mini", "predicates", @@ -865,6 +925,26 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3369f5ac52d5eb6ab48c6b4ffdc8efbcad6b89c765749064ba298f2c68a16a76" +[[package]] +name = "thiserror" +version = "1.0.58" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03468839009160513471e86a034bb2c5c0e4baae3b43f79ffc55c4a5427b3297" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.58" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c61f3ba182994efc43764a46c018c347bc492c79f024e705f46567b418f6d4f7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.58", +] + [[package]] name = "toml_datetime" version = "0.6.5" diff --git a/Cargo.toml b/Cargo.toml index 07865ab..6cf9ef9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -37,6 +37,7 @@ edition.workspace = true anyhow.workspace = true clap = { version = "4.5.4", features = ["derive"] } crossterm = "0.27.0" +dirs = "5.0.1" hashbrown = "0.14.3" notify-debouncer-mini = "0.4.1" ratatui = "0.26.1" diff --git a/src/init.rs b/src/init.rs index 459519d..d051fc4 100644 --- a/src/init.rs +++ b/src/init.rs @@ -6,7 +6,7 @@ use std::{ path::Path, }; -use crate::{embedded::EMBEDDED_FILES, info_file::ExerciseInfo}; +use crate::{embedded::EMBEDDED_FILES, info_file::ExerciseInfo, trust::trust_current_dir}; fn create_cargo_toml(exercise_infos: &[ExerciseInfo]) -> io::Result<()> { let mut cargo_toml = Vec::with_capacity(1 << 13); @@ -85,6 +85,8 @@ pub fn init(exercise_infos: &[ExerciseInfo]) -> Result<()> { create_vscode_dir().context("Failed to create the file `rustlings/.vscode/extensions.json`")?; + trust_current_dir()?; + Ok(()) } diff --git a/src/main.rs b/src/main.rs index ed5becf..7b63f70 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,5 +1,4 @@ use anyhow::{Context, Result}; -use app_state::StateFileStatus; use clap::{Parser, Subcommand}; use crossterm::{ terminal::{Clear, ClearType}, @@ -19,14 +18,16 @@ mod init; mod list; mod progress_bar; mod run; +mod trust; mod watch; use self::{ - app_state::AppState, + app_state::{AppState, StateFileStatus}, info_file::InfoFile, init::init, list::list, run::run, + trust::{current_dir_is_trusted, trust_current_dir}, watch::{watch, WatchExit}, }; @@ -61,6 +62,11 @@ enum Subcommands { /// The name of the exercise name: String, }, + /// Trust the current directory with its exercises. + /// + /// You only need to run this if you want to work on third-party exercises or after you moved + /// the official exercises that were initialized with `rustlings init`. + Trust, } fn main() -> Result<()> { @@ -72,14 +78,26 @@ fn main() -> Result<()> { if matches!(args.command, Some(Subcommands::Init)) { init(&info_file.exercises).context("Initialization failed")?; - println!("{POST_INIT_MSG}"); return Ok(()); - } else if !Path::new("exercises").is_dir() { + } + + if !Path::new("exercises").is_dir() { println!("{PRE_INIT_MSG}"); exit(1); } + if matches!(args.command, Some(Subcommands::Trust)) { + trust_current_dir()?; + println!("{POST_TRUST_MSG}"); + return Ok(()); + } + + if !current_dir_is_trusted()? { + println!("{NOT_TRUSTED_MSG}"); + exit(1); + } + let (mut app_state, state_file_status) = AppState::new( info_file.exercises, info_file.final_message.unwrap_or_default(), @@ -130,8 +148,6 @@ fn main() -> Result<()> { } } } - // `Init` is handled above. - Some(Subcommands::Init) => (), Some(Subcommands::Run { name }) => { if let Some(name) = name { app_state.set_current_exercise_by_name(&name)?; @@ -149,6 +165,8 @@ fn main() -> Result<()> { app_state.set_current_exercise_by_name(&name)?; println!("{}", app_state.current_exercise().hint); } + // `Init` and `Trust` are handled above. + Some(Subcommands::Init | Subcommands::Trust) => (), } Ok(()) @@ -158,8 +176,13 @@ const CARGO_NOT_FOUND_ERR: &str = "Failed to find `cargo`. Did you already install Rust? Try running `cargo --version` to diagnose the problem."; +const POST_INIT_MSG: &str = "Done initialization! + +Run `cd rustlings` to go into the generated directory. +Then run `rustlings` to get started."; + const PRE_INIT_MSG: &str = r" - welcome to... + Welcome to... _ _ _ _ __ _ _ ___| |_| (_)_ __ __ _ ___ | '__| | | / __| __| | | '_ \ / _` / __| @@ -170,11 +193,19 @@ const PRE_INIT_MSG: &str = r" The `exercises` directory wasn't found in the current directory. If you are just starting with Rustlings, run the command `rustlings init` to initialize it."; -const POST_INIT_MSG: &str = " -Done initialization! +const POST_TRUST_MSG: &str = "You now trust the exercises in the current directory. +Run `rustlings` to start working on them."; -Run `cd rustlings` to go into the generated directory. -Then run `rustlings` to get started."; +const NOT_TRUSTED_MSG: &str = "It looks like you are trying to work on third-party exercises. +Rustlings supports third-party exercises. But because Rustlings runs the code inside an exercise, +we need to warn you about the possibility of malicious code. +We recommend that you read all the exercise files in the `exercises` directory and check the +dependencies in the `Cargo.toml` file. +If everything looks fine and you want to trust this directory, run `rustlings trust`. + +If you you are trying to work on the official exercises that were generated using `rustlings init`, +then you probably moved the directory containing them. In that case, you can run `rustlings trust` +without a problem."; const FENISH_LINE: &str = "+----------------------------------------------------+ | You made it to the Fe-nish line! | diff --git a/src/trust.rs b/src/trust.rs new file mode 100644 index 0000000..7e36f73 --- /dev/null +++ b/src/trust.rs @@ -0,0 +1,72 @@ +use anyhow::{Context, Error, Result}; +use std::{ + env, + fs::{self, OpenOptions}, + io::{ErrorKind, Write}, +}; + +const DATA_DIR_NAME: &str = "rustlings"; +const TRUSTED_DIRS_FILE_NAME: &str = "trusted-dirs.txt"; + +pub fn trust_current_dir() -> Result<()> { + let mut path = dirs::data_dir().context("Failed to determine the data directory")?; + path.push(DATA_DIR_NAME); + if !path.is_dir() { + fs::create_dir(&path) + .with_context(|| format!("Failed to create the directory {}", path.display()))?; + } + + path.push(TRUSTED_DIRS_FILE_NAME); + let mut file = OpenOptions::new() + .create(true) + .append(true) + .open(&path) + .with_context(|| { + format!( + "Failed to create/open the file {} in write mode", + path.display(), + ) + })?; + + let dir = env::current_dir().context("Failed to get the current directory path")?; + let dir = dir.to_string_lossy(); + let mut line = Vec::with_capacity(dir.as_bytes().len() + 1); + line.extend_from_slice(dir.as_bytes()); + line.push(b'\n'); + + file.write_all(&line) + .with_context(|| format!("Failed to append to the file {}", path.display())) +} + +pub fn current_dir_is_trusted() -> Result { + let mut path = dirs::data_dir().context("Failed to determine the data directory")?; + path.push(DATA_DIR_NAME); + path.push(TRUSTED_DIRS_FILE_NAME); + + let content = match fs::read(&path) { + Ok(v) => v, + Err(e) => match e.kind() { + ErrorKind::NotFound => return Ok(false), + _ => { + return Err( + Error::from(e).context(format!("Failed to read the file {}", path.display())) + ) + } + }, + }; + + let current_dir = env::current_dir().context("Failed to get the current directory path")?; + let current_dir = current_dir.to_string_lossy(); + + for line in content.split(|c| *c == b'\n') { + if line.is_empty() { + break; + } + + if line == current_dir.as_bytes() { + return Ok(true); + } + } + + Ok(false) +} -- cgit v1.2.3 From 6f04570dd080f3aedf2fdf4fac1e627abe3a5b27 Mon Sep 17 00:00:00 2001 From: mo8it Date: Mon, 15 Apr 2024 03:36:12 +0200 Subject: Revert "Implement third-party exercises trust handling" This reverts commit 15ca847c37c170590abe6caa53dba5606d956341. See https://rust-lang.zulipchat.com/#narrow/stream/334454-rustlings/topic/Proposal.3A.20Third-party.20exercises/near/433183449 --- Cargo.lock | 80 ------------------------------------------------------------ Cargo.toml | 1 - src/init.rs | 4 +-- src/main.rs | 39 +++-------------------------- src/trust.rs | 72 ------------------------------------------------------ 5 files changed, 5 insertions(+), 191 deletions(-) delete mode 100644 src/trust.rs (limited to 'Cargo.lock') diff --git a/Cargo.lock b/Cargo.lock index 671a03f..5cfebe6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -253,27 +253,6 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6184e33543162437515c2e2b48714794e37845ec9851711914eec9d308f6ebe8" -[[package]] -name = "dirs" -version = "5.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44c45a9d03d6676652bcb5e724c7e988de1acad23a711b5217ab9cbecbec2225" -dependencies = [ - "dirs-sys", -] - -[[package]] -name = "dirs-sys" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c" -dependencies = [ - "libc", - "option-ext", - "redox_users", - "windows-sys 0.48.0", -] - [[package]] name = "doc-comment" version = "0.3.3" @@ -341,17 +320,6 @@ dependencies = [ "toml_edit", ] -[[package]] -name = "getrandom" -version = "0.2.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94b22e06ecb0110981051723910cbf0b5f5e09a2062dd7663334ee79a9d1286c" -dependencies = [ - "cfg-if", - "libc", - "wasi", -] - [[package]] name = "hashbrown" version = "0.14.3" @@ -460,16 +428,6 @@ version = "0.2.153" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" -[[package]] -name = "libredox" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" -dependencies = [ - "bitflags 2.5.0", - "libc", -] - [[package]] name = "linux-raw-sys" version = "0.4.13" @@ -570,12 +528,6 @@ version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" -[[package]] -name = "option-ext" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" - [[package]] name = "parking_lot" version = "0.12.1" @@ -682,17 +634,6 @@ dependencies = [ "bitflags 1.3.2", ] -[[package]] -name = "redox_users" -version = "0.4.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd283d9651eeda4b2a83a43c1c91b266c40fd76ecd39a50a8c630ae69dc72891" -dependencies = [ - "getrandom", - "libredox", - "thiserror", -] - [[package]] name = "regex" version = "1.10.4" @@ -743,7 +684,6 @@ dependencies = [ "assert_cmd", "clap", "crossterm", - "dirs", "hashbrown", "notify-debouncer-mini", "predicates", @@ -925,26 +865,6 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3369f5ac52d5eb6ab48c6b4ffdc8efbcad6b89c765749064ba298f2c68a16a76" -[[package]] -name = "thiserror" -version = "1.0.58" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03468839009160513471e86a034bb2c5c0e4baae3b43f79ffc55c4a5427b3297" -dependencies = [ - "thiserror-impl", -] - -[[package]] -name = "thiserror-impl" -version = "1.0.58" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c61f3ba182994efc43764a46c018c347bc492c79f024e705f46567b418f6d4f7" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.58", -] - [[package]] name = "toml_datetime" version = "0.6.5" diff --git a/Cargo.toml b/Cargo.toml index 6cf9ef9..07865ab 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -37,7 +37,6 @@ edition.workspace = true anyhow.workspace = true clap = { version = "4.5.4", features = ["derive"] } crossterm = "0.27.0" -dirs = "5.0.1" hashbrown = "0.14.3" notify-debouncer-mini = "0.4.1" ratatui = "0.26.1" diff --git a/src/init.rs b/src/init.rs index d051fc4..459519d 100644 --- a/src/init.rs +++ b/src/init.rs @@ -6,7 +6,7 @@ use std::{ path::Path, }; -use crate::{embedded::EMBEDDED_FILES, info_file::ExerciseInfo, trust::trust_current_dir}; +use crate::{embedded::EMBEDDED_FILES, info_file::ExerciseInfo}; fn create_cargo_toml(exercise_infos: &[ExerciseInfo]) -> io::Result<()> { let mut cargo_toml = Vec::with_capacity(1 << 13); @@ -85,8 +85,6 @@ pub fn init(exercise_infos: &[ExerciseInfo]) -> Result<()> { create_vscode_dir().context("Failed to create the file `rustlings/.vscode/extensions.json`")?; - trust_current_dir()?; - Ok(()) } diff --git a/src/main.rs b/src/main.rs index 7b63f70..541783d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,4 +1,5 @@ use anyhow::{Context, Result}; +use app_state::StateFileStatus; use clap::{Parser, Subcommand}; use crossterm::{ terminal::{Clear, ClearType}, @@ -18,16 +19,14 @@ mod init; mod list; mod progress_bar; mod run; -mod trust; mod watch; use self::{ - app_state::{AppState, StateFileStatus}, + app_state::AppState, info_file::InfoFile, init::init, list::list, run::run, - trust::{current_dir_is_trusted, trust_current_dir}, watch::{watch, WatchExit}, }; @@ -62,11 +61,6 @@ enum Subcommands { /// The name of the exercise name: String, }, - /// Trust the current directory with its exercises. - /// - /// You only need to run this if you want to work on third-party exercises or after you moved - /// the official exercises that were initialized with `rustlings init`. - Trust, } fn main() -> Result<()> { @@ -87,17 +81,6 @@ fn main() -> Result<()> { exit(1); } - if matches!(args.command, Some(Subcommands::Trust)) { - trust_current_dir()?; - println!("{POST_TRUST_MSG}"); - return Ok(()); - } - - if !current_dir_is_trusted()? { - println!("{NOT_TRUSTED_MSG}"); - exit(1); - } - let (mut app_state, state_file_status) = AppState::new( info_file.exercises, info_file.final_message.unwrap_or_default(), @@ -148,6 +131,8 @@ fn main() -> Result<()> { } } } + // `Init` is handled above. + Some(Subcommands::Init) => (), Some(Subcommands::Run { name }) => { if let Some(name) = name { app_state.set_current_exercise_by_name(&name)?; @@ -165,8 +150,6 @@ fn main() -> Result<()> { app_state.set_current_exercise_by_name(&name)?; println!("{}", app_state.current_exercise().hint); } - // `Init` and `Trust` are handled above. - Some(Subcommands::Init | Subcommands::Trust) => (), } Ok(()) @@ -193,20 +176,6 @@ const PRE_INIT_MSG: &str = r" The `exercises` directory wasn't found in the current directory. If you are just starting with Rustlings, run the command `rustlings init` to initialize it."; -const POST_TRUST_MSG: &str = "You now trust the exercises in the current directory. -Run `rustlings` to start working on them."; - -const NOT_TRUSTED_MSG: &str = "It looks like you are trying to work on third-party exercises. -Rustlings supports third-party exercises. But because Rustlings runs the code inside an exercise, -we need to warn you about the possibility of malicious code. -We recommend that you read all the exercise files in the `exercises` directory and check the -dependencies in the `Cargo.toml` file. -If everything looks fine and you want to trust this directory, run `rustlings trust`. - -If you you are trying to work on the official exercises that were generated using `rustlings init`, -then you probably moved the directory containing them. In that case, you can run `rustlings trust` -without a problem."; - const FENISH_LINE: &str = "+----------------------------------------------------+ | You made it to the Fe-nish line! | +-------------------------- ------------------------+ diff --git a/src/trust.rs b/src/trust.rs deleted file mode 100644 index 7e36f73..0000000 --- a/src/trust.rs +++ /dev/null @@ -1,72 +0,0 @@ -use anyhow::{Context, Error, Result}; -use std::{ - env, - fs::{self, OpenOptions}, - io::{ErrorKind, Write}, -}; - -const DATA_DIR_NAME: &str = "rustlings"; -const TRUSTED_DIRS_FILE_NAME: &str = "trusted-dirs.txt"; - -pub fn trust_current_dir() -> Result<()> { - let mut path = dirs::data_dir().context("Failed to determine the data directory")?; - path.push(DATA_DIR_NAME); - if !path.is_dir() { - fs::create_dir(&path) - .with_context(|| format!("Failed to create the directory {}", path.display()))?; - } - - path.push(TRUSTED_DIRS_FILE_NAME); - let mut file = OpenOptions::new() - .create(true) - .append(true) - .open(&path) - .with_context(|| { - format!( - "Failed to create/open the file {} in write mode", - path.display(), - ) - })?; - - let dir = env::current_dir().context("Failed to get the current directory path")?; - let dir = dir.to_string_lossy(); - let mut line = Vec::with_capacity(dir.as_bytes().len() + 1); - line.extend_from_slice(dir.as_bytes()); - line.push(b'\n'); - - file.write_all(&line) - .with_context(|| format!("Failed to append to the file {}", path.display())) -} - -pub fn current_dir_is_trusted() -> Result { - let mut path = dirs::data_dir().context("Failed to determine the data directory")?; - path.push(DATA_DIR_NAME); - path.push(TRUSTED_DIRS_FILE_NAME); - - let content = match fs::read(&path) { - Ok(v) => v, - Err(e) => match e.kind() { - ErrorKind::NotFound => return Ok(false), - _ => { - return Err( - Error::from(e).context(format!("Failed to read the file {}", path.display())) - ) - } - }, - }; - - let current_dir = env::current_dir().context("Failed to get the current directory path")?; - let current_dir = current_dir.to_string_lossy(); - - for line in content.split(|c| *c == b'\n') { - if line.is_empty() { - break; - } - - if line == current_dir.as_bytes() { - return Ok(true); - } - } - - Ok(false) -} -- cgit v1.2.3 From f5eaa578b9479d6a0b9acab5ebf155fcd50b9b27 Mon Sep 17 00:00:00 2001 From: mo8it Date: Mon, 15 Apr 2024 23:35:30 +0200 Subject: Update deps --- Cargo.lock | 37 +++++++++++++------------------------ Cargo.toml | 2 +- 2 files changed, 14 insertions(+), 25 deletions(-) (limited to 'Cargo.lock') diff --git a/Cargo.lock b/Cargo.lock index 5cfebe6..defdacd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -179,7 +179,7 @@ dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.58", + "syn", ] [[package]] @@ -589,9 +589,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.79" +version = "1.0.80" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e835ff2298f5721608eb1a980ecaee1aef2c132bf95ecc026a11b7bf3c01c02e" +checksum = "a56dea16b0a29e94408b9aa5e2940a4eedbd128a1ba20e8f7ae60fd3d465af0e" dependencies = [ "unicode-ident", ] @@ -607,9 +607,9 @@ dependencies = [ [[package]] name = "ratatui" -version = "0.26.1" +version = "0.26.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bcb12f8fbf6c62614b0d56eb352af54f6a22410c3b079eb53ee93c7b97dd31d8" +checksum = "a564a852040e82671dc50a37d88f3aa83bbc690dfc6844cfe7a2591620206a80" dependencies = [ "bitflags 2.5.0", "cassowary", @@ -745,7 +745,7 @@ checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.58", + "syn", ] [[package]] @@ -795,12 +795,12 @@ checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" [[package]] name = "stability" -version = "0.1.1" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebd1b177894da2a2d9120208c3386066af06a488255caabc5de8ddca22dbc3ce" +checksum = "2ff9eaf853dec4c8802325d8b6d3dffa86cc707fd7a1a4cdbf416e13b061787a" dependencies = [ "quote", - "syn 1.0.109", + "syn", ] [[package]] @@ -834,25 +834,14 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.58", -] - -[[package]] -name = "syn" -version = "1.0.109" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", + "syn", ] [[package]] name = "syn" -version = "2.0.58" +version = "2.0.59" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44cfb93f38070beee36b3fef7d4f5a16f27751d94b187b666a5cc5e9b0d30687" +checksum = "4a6531ffc7b071655e4ce2e04bd464c4830bb585a61cabb96cf808f05172615a" dependencies = [ "proc-macro2", "quote", @@ -1156,5 +1145,5 @@ checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.58", + "syn", ] diff --git a/Cargo.toml b/Cargo.toml index 07865ab..efc1441 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -39,7 +39,7 @@ clap = { version = "4.5.4", features = ["derive"] } crossterm = "0.27.0" hashbrown = "0.14.3" notify-debouncer-mini = "0.4.1" -ratatui = "0.26.1" +ratatui = "0.26.2" rustlings-macros = { path = "rustlings-macros" } serde.workspace = true toml_edit.workspace = true -- cgit v1.2.3 From f9be652b3b84dfa16265ec5804c267c66d381da5 Mon Sep 17 00:00:00 2001 From: mo8it Date: Tue, 16 Apr 2024 03:56:08 +0200 Subject: Ready to publish --- Cargo.lock | 4 ++-- Cargo.toml | 9 ++++++++- 2 files changed, 10 insertions(+), 3 deletions(-) (limited to 'Cargo.lock') diff --git a/Cargo.lock b/Cargo.lock index defdacd..65d2216 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -678,7 +678,7 @@ dependencies = [ [[package]] name = "rustlings" -version = "6.0.0" +version = "6.0.0-alpha.0" dependencies = [ "anyhow", "assert_cmd", @@ -696,7 +696,7 @@ dependencies = [ [[package]] name = "rustlings-macros" -version = "6.0.0" +version = "6.0.0-alpha.0" dependencies = [ "quote", ] diff --git a/Cargo.toml b/Cargo.toml index 81e91ad..06fbbf7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -33,6 +33,13 @@ version.workspace = true authors.workspace = true license.workspace = true edition.workspace = true +include = [ + "/exercises/", + "/info.toml", + "/LICENSE", + "/README.md", + "/src/", +] [dependencies] anyhow.workspace = true @@ -41,7 +48,7 @@ crossterm = "0.27.0" hashbrown = "0.14.3" notify-debouncer-mini = "0.4.1" ratatui = "0.26.2" -rustlings-macros = "6.0.0-alpha.0" +rustlings-macros = { path = "rustlings-macros", version = "6.0.0-alpha.0" } serde.workspace = true toml_edit.workspace = true which = "6.0.1" -- cgit v1.2.3 From 501b973c258a3c2e3a463d58c16402302184380f Mon Sep 17 00:00:00 2001 From: mo8it Date: Wed, 17 Apr 2024 15:55:50 +0200 Subject: Add "dev update" --- Cargo.lock | 9 ----- Cargo.toml | 14 ++------ dev/Cargo.toml | 4 +-- gen-dev-cargo-toml/Cargo.toml | 10 ------ gen-dev-cargo-toml/src/main.rs | 68 ----------------------------------- src/dev.rs | 9 ++--- src/dev/check.rs | 81 ++++++++++++++++++++++++++++++++++++++---- src/dev/init.rs | 23 ++++++------ src/dev/update.rs | 53 +++++++++++++++++++++++++++ src/exercise.rs | 5 ++- src/init.rs | 41 +++++++-------------- src/main.rs | 27 +++++++++----- tests/dev_cargo_bins.rs | 44 ----------------------- 13 files changed, 182 insertions(+), 206 deletions(-) delete mode 100644 gen-dev-cargo-toml/Cargo.toml delete mode 100644 gen-dev-cargo-toml/src/main.rs create mode 100644 src/dev/update.rs delete mode 100644 tests/dev_cargo_bins.rs (limited to 'Cargo.lock') diff --git a/Cargo.lock b/Cargo.lock index 65d2216..4c95ca8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -311,15 +311,6 @@ dependencies = [ "libc", ] -[[package]] -name = "gen-dev-cargo-toml" -version = "0.0.0" -dependencies = [ - "anyhow", - "serde", - "toml_edit", -] - [[package]] name = "hashbrown" version = "0.14.3" diff --git a/Cargo.toml b/Cargo.toml index 06fbbf7..cde4182 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,9 +6,6 @@ exclude = [ "tests/fixture/success", "dev", ] -members = [ - "gen-dev-cargo-toml", -] [workspace.package] version = "6.0.0-alpha.0" @@ -20,11 +17,6 @@ authors = [ license = "MIT" edition = "2021" -[workspace.dependencies] -anyhow = "1.0.82" -serde = { version = "1.0.197", features = ["derive"] } -toml_edit = { version = "0.22.9", default-features = false, features = ["parse", "serde"] } - [package] name = "rustlings" description = "Small exercises to get you used to reading and writing Rust code!" @@ -42,15 +34,15 @@ include = [ ] [dependencies] -anyhow.workspace = true +anyhow = "1.0.82" clap = { version = "4.5.4", features = ["derive"] } crossterm = "0.27.0" hashbrown = "0.14.3" notify-debouncer-mini = "0.4.1" ratatui = "0.26.2" rustlings-macros = { path = "rustlings-macros", version = "6.0.0-alpha.0" } -serde.workspace = true -toml_edit.workspace = true +serde = { version = "1.0.197", features = ["derive"] } +toml_edit = { version = "0.22.9", default-features = false, features = ["parse", "serde"] } which = "6.0.1" [dev-dependencies] diff --git a/dev/Cargo.toml b/dev/Cargo.toml index 1d230eb..e66973e 100644 --- a/dev/Cargo.toml +++ b/dev/Cargo.toml @@ -1,6 +1,4 @@ -# This file is a hack to allow using `cargo run` to test `rustlings` during development. -# You shouldn't edit it manually. It is created and updated by running `cargo run -p gen-dev-cargo-toml`. - +# Don't edit the `bin` list manually! It is updated by `cargo run -- dev update` bin = [ { name = "intro1", path = "../exercises/00_intro/intro1.rs" }, { name = "intro2", path = "../exercises/00_intro/intro2.rs" }, diff --git a/gen-dev-cargo-toml/Cargo.toml b/gen-dev-cargo-toml/Cargo.toml deleted file mode 100644 index 8922ae8..0000000 --- a/gen-dev-cargo-toml/Cargo.toml +++ /dev/null @@ -1,10 +0,0 @@ -[package] -name = "gen-dev-cargo-toml" -publish = false -license.workspace = true -edition.workspace = true - -[dependencies] -anyhow.workspace = true -serde.workspace = true -toml_edit.workspace = true diff --git a/gen-dev-cargo-toml/src/main.rs b/gen-dev-cargo-toml/src/main.rs deleted file mode 100644 index 43b4ebd..0000000 --- a/gen-dev-cargo-toml/src/main.rs +++ /dev/null @@ -1,68 +0,0 @@ -// Generates `dev/Cargo.toml` such that it is synced with `info.toml`. -// `dev/Cargo.toml` is a hack to allow using `cargo run` to test `rustlings` -// during development. - -use anyhow::{bail, Context, Result}; -use serde::Deserialize; -use std::{ - fs::{self, create_dir}, - io::ErrorKind, -}; - -#[derive(Deserialize)] -struct ExerciseInfo { - name: String, - dir: Option, -} - -#[derive(Deserialize)] -struct InfoFile { - exercises: Vec, -} - -fn main() -> Result<()> { - let exercise_infos = toml_edit::de::from_str::( - &fs::read_to_string("info.toml").context("Failed to read `info.toml`")?, - ) - .context("Failed to deserialize `info.toml`")? - .exercises; - - let mut buf = Vec::with_capacity(1 << 14); - - buf.extend_from_slice( - b"# This file is a hack to allow using `cargo run` to test `rustlings` during development. -# You shouldn't edit it manually. It is created and updated by running `cargo run -p gen-dev-cargo-toml`. - -bin = [\n", - ); - - for exercise_info in exercise_infos { - buf.extend_from_slice(b" { name = \""); - buf.extend_from_slice(exercise_info.name.as_bytes()); - buf.extend_from_slice(b"\", path = \"../exercises/"); - if let Some(dir) = &exercise_info.dir { - buf.extend_from_slice(dir.as_bytes()); - buf.push(b'/'); - } - buf.extend_from_slice(exercise_info.name.as_bytes()); - buf.extend_from_slice(b".rs\" },\n"); - } - - buf.extend_from_slice( - br#"] - -[package] -name = "rustlings-dev" -edition = "2021" -publish = false -"#, - ); - - if let Err(e) = create_dir("dev") { - if e.kind() != ErrorKind::AlreadyExists { - bail!("Failed to create the `dev` directory: {e}"); - } - } - - fs::write("dev/Cargo.toml", buf).context("Failed to write `dev/Cargo.toml`") -} diff --git a/src/dev.rs b/src/dev.rs index 7905f38..feca99e 100644 --- a/src/dev.rs +++ b/src/dev.rs @@ -1,22 +1,23 @@ use anyhow::{Context, Result}; use clap::Subcommand; -use crate::info_file::InfoFile; - mod check; mod init; +mod update; #[derive(Subcommand)] pub enum DevCommands { Init, Check, + Update, } impl DevCommands { - pub fn run(self, info_file: InfoFile) -> Result<()> { + pub fn run(self) -> Result<()> { match self { DevCommands::Init => init::init().context(INIT_ERR), - DevCommands::Check => check::check(info_file), + DevCommands::Check => check::check(), + DevCommands::Update => update::update(), } } } diff --git a/src/dev/check.rs b/src/dev/check.rs index 5910a75..bc8e459 100644 --- a/src/dev/check.rs +++ b/src/dev/check.rs @@ -1,16 +1,83 @@ +use anyhow::{bail, Context, Result}; use std::fs; -use anyhow::{Context, Result}; +use crate::{ + info_file::{ExerciseInfo, InfoFile}, + DEVELOPING_OFFIFICAL_RUSTLINGS, +}; -use crate::{info_file::InfoFile, init::cargo_toml}; +pub fn bins_start_end_ind(cargo_toml: &str) -> Result<(usize, usize)> { + let start_ind = cargo_toml + .find("bin = [") + .context("Failed to find the start of the `bin` list (`bin = [`)")? + + 7; + let end_ind = start_ind + + cargo_toml + .get(start_ind..) + .and_then(|slice| slice.as_bytes().iter().position(|c| *c == b']')) + .context("Failed to find the end of the `bin` list (`]`)")?; + + Ok((start_ind, end_ind)) +} + +pub fn append_bins( + buf: &mut Vec, + exercise_infos: &[ExerciseInfo], + exercise_path_prefix: &[u8], +) { + buf.push(b'\n'); + for exercise_info in exercise_infos { + buf.extend_from_slice(b" { name = \""); + buf.extend_from_slice(exercise_info.name.as_bytes()); + buf.extend_from_slice(b"\", path = \""); + buf.extend_from_slice(exercise_path_prefix); + buf.extend_from_slice(b"exercises/"); + if let Some(dir) = &exercise_info.dir { + buf.extend_from_slice(dir.as_bytes()); + buf.push(b'/'); + } + buf.extend_from_slice(exercise_info.name.as_bytes()); + buf.extend_from_slice(b".rs\" },\n"); + } +} + +fn check_cargo_toml( + exercise_infos: &[ExerciseInfo], + current_cargo_toml: &str, + exercise_path_prefix: &[u8], +) -> Result<()> { + let (bins_start_ind, bins_end_ind) = bins_start_end_ind(current_cargo_toml)?; + + let old_bins = ¤t_cargo_toml.as_bytes()[bins_start_ind..bins_end_ind]; + let mut new_bins = Vec::with_capacity(1 << 13); + append_bins(&mut new_bins, exercise_infos, exercise_path_prefix); + + if old_bins != new_bins { + bail!("`Cargo.toml` is outdated. Run `rustlings dev update` to update it"); + } + + Ok(()) +} + +pub fn check() -> Result<()> { + let info_file = InfoFile::parse()?; -pub fn check(info_file: InfoFile) -> Result<()> { // TODO: Add checks - // TODO: Keep dependencies! - fs::write("Cargo.toml", cargo_toml(&info_file.exercises)) - .context("Failed to update the file `Cargo.toml`")?; - println!("Updated `Cargo.toml`"); + if DEVELOPING_OFFIFICAL_RUSTLINGS { + check_cargo_toml( + &info_file.exercises, + include_str!("../../dev/Cargo.toml"), + b"../", + ) + .context("The file `dev/Cargo.toml` is outdated. Please run `cargo run -- dev update` to update it")?; + } else { + let current_cargo_toml = + fs::read_to_string("Cargo.toml").context("Failed to read the file `Cargo.toml`")?; + check_cargo_toml(&info_file.exercises, ¤t_cargo_toml, b"").context( + "The file `Cargo.toml` is outdated. Please run `rustlings dev update` to update it", + )?; + } println!("\nEverything looks fine!"); diff --git a/src/dev/init.rs b/src/dev/init.rs index 0993522..3ce5055 100644 --- a/src/dev/init.rs +++ b/src/dev/init.rs @@ -1,6 +1,5 @@ -use std::fs::{self, create_dir}; - use anyhow::{Context, Result}; +use std::fs::{self, create_dir}; use crate::CURRENT_FORMAT_VERSION; @@ -19,11 +18,8 @@ pub fn init() -> Result<()> { ) .context("Failed to create the file `rustlings/info.toml`")?; - fs::write( - "rustlings/Cargo.toml", - format!("{CARGO_TOML_COMMENT}{}", crate::init::CARGO_TOML_PACKAGE), - ) - .context("Failed to create the file `rustlings/Cargo.toml`")?; + fs::write("rustlings/Cargo.toml", CARGO_TOML) + .context("Failed to create the file `rustlings/Cargo.toml`")?; fs::write("rustlings/.gitignore", crate::init::GITIGNORE) .context("Failed to create the file `rustlings/.gitignore`")?; @@ -80,10 +76,17 @@ mode = "test" hint = """???""" "#; -const CARGO_TOML_COMMENT: &str = - "# You shouldn't edit this file manually! It is updated by `rustlings dev check` +const CARGO_TOML: &[u8] = + br#"# Don't edit the `bin` list manually! It is updated by `rustlings dev update` +bin = [] -"; +[package] +name = "rustlings" +edition = "2021" +publish = false + +[dependencies] +"#; const README: &str = "# Rustlings 🦀 diff --git a/src/dev/update.rs b/src/dev/update.rs new file mode 100644 index 0000000..981934d --- /dev/null +++ b/src/dev/update.rs @@ -0,0 +1,53 @@ +use std::fs; + +use anyhow::{Context, Result}; + +use crate::{ + info_file::{ExerciseInfo, InfoFile}, + DEVELOPING_OFFIFICAL_RUSTLINGS, +}; + +use super::check::{append_bins, bins_start_end_ind}; + +fn update_cargo_toml( + exercise_infos: &[ExerciseInfo], + current_cargo_toml: &str, + cargo_toml_path: &str, + exercise_path_prefix: &[u8], +) -> Result<()> { + let (bins_start_ind, bins_end_ind) = bins_start_end_ind(current_cargo_toml)?; + + let mut new_cargo_toml = Vec::with_capacity(1 << 13); + new_cargo_toml.extend_from_slice(current_cargo_toml[..bins_start_ind].as_bytes()); + append_bins(&mut new_cargo_toml, exercise_infos, exercise_path_prefix); + new_cargo_toml.extend_from_slice(current_cargo_toml[bins_end_ind..].as_bytes()); + + fs::write(cargo_toml_path, new_cargo_toml).context("Failed to write the `Cargo.toml` file")?; + + Ok(()) +} + +pub fn update() -> Result<()> { + let info_file = InfoFile::parse()?; + + if DEVELOPING_OFFIFICAL_RUSTLINGS { + update_cargo_toml( + &info_file.exercises, + include_str!("../../dev/Cargo.toml"), + "dev/Cargo.toml", + b"../", + ) + .context("Failed to update the file `dev/Cargo.toml`")?; + + println!("Updated `dev/Cargo.toml`"); + } else { + let current_cargo_toml = + fs::read_to_string("Cargo.toml").context("Failed to read the file `Cargo.toml`")?; + update_cargo_toml(&info_file.exercises, ¤t_cargo_toml, "Cargo.toml", b"") + .context("Failed to update the file `Cargo.toml`")?; + + println!("Updated `Cargo.toml`"); + } + + Ok(()) +} diff --git a/src/exercise.rs b/src/exercise.rs index 8bdf399..c4df999 100644 --- a/src/exercise.rs +++ b/src/exercise.rs @@ -9,6 +9,7 @@ use std::{ use crate::{ embedded::{WriteStrategy, EMBEDDED_FILES}, info_file::Mode, + DEVELOPING_OFFIFICAL_RUSTLINGS, }; pub struct TerminalFileLink<'a> { @@ -50,9 +51,7 @@ impl Exercise { cmd.arg(command); // A hack to make `cargo run` work when developing Rustlings. - // Use `dev/Cargo.toml` when in the directory of the repository. - #[cfg(debug_assertions)] - if std::path::Path::new("tests").exists() { + if DEVELOPING_OFFIFICAL_RUSTLINGS { cmd.arg("--manifest-path").arg("dev/Cargo.toml"); } diff --git a/src/init.rs b/src/init.rs index 5fa44d4..52315e2 100644 --- a/src/init.rs +++ b/src/init.rs @@ -6,30 +6,19 @@ use std::{ path::Path, }; -use crate::{embedded::EMBEDDED_FILES, info_file::ExerciseInfo}; - -pub fn cargo_toml(exercise_infos: &[ExerciseInfo]) -> Vec { - let mut cargo_toml = Vec::with_capacity(1 << 13); - cargo_toml.extend_from_slice(b"bin = [\n"); - for exercise_info in exercise_infos { - cargo_toml.extend_from_slice(b" { name = \""); - cargo_toml.extend_from_slice(exercise_info.name.as_bytes()); - cargo_toml.extend_from_slice(b"\", path = \"exercises/"); - if let Some(dir) = &exercise_info.dir { - cargo_toml.extend_from_slice(dir.as_bytes()); - cargo_toml.push(b'/'); - } - cargo_toml.extend_from_slice(exercise_info.name.as_bytes()); - cargo_toml.extend_from_slice(b".rs\" },\n"); +use crate::embedded::EMBEDDED_FILES; + +const CARGO_TOML: &[u8] = { + let cargo_toml = include_bytes!("../dev/Cargo.toml"); + // Skip the first line (comment). + let mut start_ind = 0; + while cargo_toml[start_ind] != b'\n' { + start_ind += 1; } + cargo_toml.split_at(start_ind + 1).1 +}; - cargo_toml.extend_from_slice(b"]\n\n"); - cargo_toml.extend_from_slice(CARGO_TOML_PACKAGE.as_bytes()); - - cargo_toml -} - -pub fn init(exercise_infos: &[ExerciseInfo]) -> Result<()> { +pub fn init() -> Result<()> { if Path::new("exercises").is_dir() && Path::new("Cargo.toml").is_file() { bail!(PROBABLY_IN_RUSTLINGS_DIR_ERR); } @@ -49,7 +38,7 @@ pub fn init(exercise_infos: &[ExerciseInfo]) -> Result<()> { .init_exercises_dir() .context("Failed to initialize the `rustlings/exercises` directory")?; - fs::write("Cargo.toml", cargo_toml(exercise_infos)) + fs::write("Cargo.toml", CARGO_TOML) .context("Failed to create the file `rustlings/Cargo.toml`")?; fs::write(".gitignore", GITIGNORE) @@ -64,12 +53,6 @@ pub fn init(exercise_infos: &[ExerciseInfo]) -> Result<()> { Ok(()) } -pub const CARGO_TOML_PACKAGE: &str = r#"[package] -name = "rustlings" -edition = "2021" -publish = false -"#; - pub const GITIGNORE: &[u8] = b"Cargo.lock .rustlings-state.txt target diff --git a/src/main.rs b/src/main.rs index 8b3f28f..ea5f7c9 100644 --- a/src/main.rs +++ b/src/main.rs @@ -25,6 +25,17 @@ mod watch; use self::{app_state::AppState, dev::DevCommands, info_file::InfoFile, watch::WatchExit}; const CURRENT_FORMAT_VERSION: u8 = 1; +const DEVELOPING_OFFIFICAL_RUSTLINGS: bool = { + #[allow(unused_assignments, unused_mut)] + let mut debug_profile = false; + + #[cfg(debug_assertions)] + { + debug_profile = true; + } + + debug_profile +}; /// Rustlings is a collection of small exercises to get you used to writing and reading Rust code #[derive(Parser)] @@ -66,17 +77,11 @@ fn main() -> Result<()> { which::which("cargo").context(CARGO_NOT_FOUND_ERR)?; - let info_file = InfoFile::parse()?; - - if info_file.format_version > CURRENT_FORMAT_VERSION { - bail!(FORMAT_VERSION_HIGHER_ERR); - } - match args.command { Some(Subcommands::Init) => { - return init::init(&info_file.exercises).context("Initialization failed"); + return init::init().context("Initialization failed"); } - Some(Subcommands::Dev(dev_command)) => return dev_command.run(info_file), + Some(Subcommands::Dev(dev_command)) => return dev_command.run(), _ => (), } @@ -85,6 +90,12 @@ fn main() -> Result<()> { exit(1); } + let info_file = InfoFile::parse()?; + + if info_file.format_version > CURRENT_FORMAT_VERSION { + bail!(FORMAT_VERSION_HIGHER_ERR); + } + let (mut app_state, state_file_status) = AppState::new( info_file.exercises, info_file.final_message.unwrap_or_default(), diff --git a/tests/dev_cargo_bins.rs b/tests/dev_cargo_bins.rs deleted file mode 100644 index 81f48b1..0000000 --- a/tests/dev_cargo_bins.rs +++ /dev/null @@ -1,44 +0,0 @@ -// Makes sure that `dev/Cargo.toml` is synced with `info.toml`. -// When this test fails, you just need to run `cargo run -p gen-dev-cargo-toml`. - -use serde::Deserialize; -use std::fs; - -#[derive(Deserialize)] -struct ExerciseInfo { - name: String, - dir: Option, -} - -#[derive(Deserialize)] -struct InfoFile { - exercises: Vec, -} - -#[test] -fn dev_cargo_bins() { - let cargo_toml = fs::read_to_string("dev/Cargo.toml").unwrap(); - - let exercise_infos = - toml_edit::de::from_str::(&fs::read_to_string("info.toml").unwrap()) - .unwrap() - .exercises; - - let mut start_ind = 0; - for exercise_info in exercise_infos { - let name_start = start_ind + cargo_toml[start_ind..].find('"').unwrap() + 1; - let name_end = name_start + cargo_toml[name_start..].find('"').unwrap(); - assert_eq!(exercise_info.name, &cargo_toml[name_start..name_end]); - - let path_start = name_end + cargo_toml[name_end + 1..].find('"').unwrap() + 2; - let path_end = path_start + cargo_toml[path_start..].find('"').unwrap(); - let expected_path = if let Some(dir) = exercise_info.dir { - format!("../exercises/{dir}/{}.rs", exercise_info.name) - } else { - format!("../exercises/{}.rs", exercise_info.name) - }; - assert_eq!(expected_path, &cargo_toml[path_start..path_end]); - - start_ind = path_end + 1; - } -} -- cgit v1.2.3 From a2506f154b929b74e7d5c286cee6eabb008aa1fd Mon Sep 17 00:00:00 2001 From: mo8it Date: Wed, 17 Apr 2024 15:56:24 +0200 Subject: Update serde --- Cargo.lock | 12 ++++++------ Cargo.toml | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) (limited to 'Cargo.lock') diff --git a/Cargo.lock b/Cargo.lock index 4c95ca8..b194de6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -580,9 +580,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.80" +version = "1.0.81" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a56dea16b0a29e94408b9aa5e2940a4eedbd128a1ba20e8f7ae60fd3d465af0e" +checksum = "3d1597b0c024618f09a9c3b8655b7e430397a36d23fdafec26d6965e9eec3eba" dependencies = [ "unicode-ident", ] @@ -721,18 +721,18 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "serde" -version = "1.0.197" +version = "1.0.198" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fb1c873e1b9b056a4dc4c0c198b24c3ffa059243875552b2bd0933b1aee4ce2" +checksum = "9846a40c979031340571da2545a4e5b7c4163bdae79b301d5f86d03979451fcc" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.197" +version = "1.0.198" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b" +checksum = "e88edab869b01783ba905e7d0153f9fc1a6505a96e4ad3018011eedb838566d9" dependencies = [ "proc-macro2", "quote", diff --git a/Cargo.toml b/Cargo.toml index cde4182..5e3be11 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -41,7 +41,7 @@ hashbrown = "0.14.3" notify-debouncer-mini = "0.4.1" ratatui = "0.26.2" rustlings-macros = { path = "rustlings-macros", version = "6.0.0-alpha.0" } -serde = { version = "1.0.197", features = ["derive"] } +serde = { version = "1.0.198", features = ["derive"] } toml_edit = { version = "0.22.9", default-features = false, features = ["parse", "serde"] } which = "6.0.1" -- cgit v1.2.3 From 04d36996dd745af284be380bd902e0bad491b87a Mon Sep 17 00:00:00 2001 From: mo8it Date: Sun, 21 Apr 2024 19:27:00 +0200 Subject: Update deps --- Cargo.lock | 16 ++++++++-------- Cargo.toml | 2 +- 2 files changed, 9 insertions(+), 9 deletions(-) (limited to 'Cargo.lock') diff --git a/Cargo.lock b/Cargo.lock index b194de6..fdc7936 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -656,9 +656,9 @@ checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56" [[package]] name = "rustix" -version = "0.38.32" +version = "0.38.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65e04861e65f21776e67888bfbea442b3642beaa0138fdb1dd7a84a52dffdb89" +checksum = "e3cc72858054fcff6d7dea32df2aeaee6a7c24227366d7ea429aada2f26b16ad" dependencies = [ "bitflags 2.5.0", "errno", @@ -771,9 +771,9 @@ dependencies = [ [[package]] name = "signal-hook-registry" -version = "1.4.1" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" +checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" dependencies = [ "libc", ] @@ -830,9 +830,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.59" +version = "2.0.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a6531ffc7b071655e4ce2e04bd464c4830bb585a61cabb96cf808f05172615a" +checksum = "909518bc7b1c9b779f1bbf07f2929d35af9f0f37e47c6e9ef7f9dddc1e1821f3" dependencies = [ "proc-macro2", "quote", @@ -856,9 +856,9 @@ dependencies = [ [[package]] name = "toml_edit" -version = "0.22.9" +version = "0.22.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e40bb779c5187258fd7aad0eb68cb8706a0a81fa712fbea808ab43c4b8374c4" +checksum = "d3328d4f68a705b2a4498da1d580585d39a6510f98318a2cec3018a7ec61ddef" dependencies = [ "indexmap", "serde", diff --git a/Cargo.toml b/Cargo.toml index 5e3be11..6181b1e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -42,7 +42,7 @@ notify-debouncer-mini = "0.4.1" ratatui = "0.26.2" rustlings-macros = { path = "rustlings-macros", version = "6.0.0-alpha.0" } serde = { version = "1.0.198", features = ["derive"] } -toml_edit = { version = "0.22.9", default-features = false, features = ["parse", "serde"] } +toml_edit = { version = "0.22.12", default-features = false, features = ["parse", "serde"] } which = "6.0.1" [dev-dependencies] -- cgit v1.2.3 From e5a19a4c33e16e517e4d597acb721ed281c7bdae Mon Sep 17 00:00:00 2001 From: mo8it Date: Tue, 23 Apr 2024 15:32:07 +0200 Subject: Update deps --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'Cargo.lock') diff --git a/Cargo.lock b/Cargo.lock index fdc7936..1618eec 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -656,9 +656,9 @@ checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56" [[package]] name = "rustix" -version = "0.38.33" +version = "0.38.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3cc72858054fcff6d7dea32df2aeaee6a7c24227366d7ea429aada2f26b16ad" +checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" dependencies = [ "bitflags 2.5.0", "errno", -- cgit v1.2.3 From 2dac8e509bed07c30a98983cfb6b80f73a1582e9 Mon Sep 17 00:00:00 2001 From: mo8it Date: Tue, 23 Apr 2024 19:18:25 +0200 Subject: Refactor embedded files to add solutions --- Cargo.lock | 2 + Cargo.toml | 8 +- info.toml | 3 + rustlings-macros/Cargo.toml | 2 + rustlings-macros/src/lib.rs | 99 ++++++-------------- solutions/00_intro/intro1.rs | 1 + solutions/00_intro/intro2.rs | 1 + solutions/01_variables/variables1.rs | 1 + solutions/01_variables/variables2.rs | 1 + solutions/01_variables/variables3.rs | 1 + solutions/01_variables/variables4.rs | 1 + solutions/01_variables/variables5.rs | 1 + solutions/01_variables/variables6.rs | 1 + solutions/02_functions/functions1.rs | 1 + solutions/02_functions/functions2.rs | 1 + solutions/02_functions/functions3.rs | 1 + solutions/02_functions/functions4.rs | 1 + solutions/02_functions/functions5.rs | 1 + solutions/03_if/if1.rs | 1 + solutions/03_if/if2.rs | 1 + solutions/03_if/if3.rs | 1 + solutions/04_primitive_types/primitive_types1.rs | 1 + solutions/04_primitive_types/primitive_types2.rs | 1 + solutions/04_primitive_types/primitive_types3.rs | 1 + solutions/04_primitive_types/primitive_types4.rs | 1 + solutions/04_primitive_types/primitive_types5.rs | 1 + solutions/04_primitive_types/primitive_types6.rs | 1 + solutions/05_vecs/vecs1.rs | 1 + solutions/05_vecs/vecs2.rs | 1 + solutions/06_move_semantics/move_semantics1.rs | 1 + solutions/06_move_semantics/move_semantics2.rs | 1 + solutions/06_move_semantics/move_semantics3.rs | 1 + solutions/06_move_semantics/move_semantics4.rs | 1 + solutions/06_move_semantics/move_semantics5.rs | 1 + solutions/06_move_semantics/move_semantics6.rs | 1 + solutions/07_structs/structs1.rs | 1 + solutions/07_structs/structs2.rs | 1 + solutions/07_structs/structs3.rs | 1 + solutions/08_enums/enums1.rs | 1 + solutions/08_enums/enums2.rs | 1 + solutions/08_enums/enums3.rs | 1 + solutions/09_strings/strings1.rs | 1 + solutions/09_strings/strings2.rs | 1 + solutions/09_strings/strings3.rs | 1 + solutions/09_strings/strings4.rs | 1 + solutions/10_modules/modules1.rs | 1 + solutions/10_modules/modules2.rs | 1 + solutions/10_modules/modules3.rs | 1 + solutions/11_hashmaps/hashmaps1.rs | 1 + solutions/11_hashmaps/hashmaps2.rs | 1 + solutions/11_hashmaps/hashmaps3.rs | 1 + solutions/12_options/options1.rs | 1 + solutions/12_options/options2.rs | 1 + solutions/12_options/options3.rs | 1 + solutions/13_error_handling/errors1.rs | 1 + solutions/13_error_handling/errors2.rs | 1 + solutions/13_error_handling/errors3.rs | 1 + solutions/13_error_handling/errors4.rs | 1 + solutions/13_error_handling/errors5.rs | 1 + solutions/13_error_handling/errors6.rs | 1 + solutions/14_generics/generics1.rs | 1 + solutions/14_generics/generics2.rs | 1 + solutions/15_traits/traits1.rs | 1 + solutions/15_traits/traits2.rs | 1 + solutions/15_traits/traits3.rs | 1 + solutions/15_traits/traits4.rs | 1 + solutions/15_traits/traits5.rs | 1 + solutions/16_lifetimes/lifetimes1.rs | 1 + solutions/16_lifetimes/lifetimes2.rs | 1 + solutions/16_lifetimes/lifetimes3.rs | 1 + solutions/17_tests/tests1.rs | 1 + solutions/17_tests/tests2.rs | 1 + solutions/17_tests/tests3.rs | 1 + solutions/17_tests/tests4.rs | 1 + solutions/18_iterators/iterators1.rs | 1 + solutions/18_iterators/iterators2.rs | 1 + solutions/18_iterators/iterators3.rs | 1 + solutions/18_iterators/iterators4.rs | 1 + solutions/18_iterators/iterators5.rs | 1 + solutions/19_smart_pointers/arc1.rs | 1 + solutions/19_smart_pointers/box1.rs | 1 + solutions/19_smart_pointers/cow1.rs | 1 + solutions/19_smart_pointers/rc1.rs | 1 + solutions/20_threads/threads1.rs | 1 + solutions/20_threads/threads2.rs | 1 + solutions/20_threads/threads3.rs | 1 + solutions/21_macros/macros1.rs | 1 + solutions/21_macros/macros2.rs | 1 + solutions/21_macros/macros3.rs | 1 + solutions/21_macros/macros4.rs | 1 + solutions/22_clippy/clippy1.rs | 1 + solutions/22_clippy/clippy2.rs | 1 + solutions/22_clippy/clippy3.rs | 1 + solutions/23_conversions/as_ref_mut.rs | 1 + solutions/23_conversions/from_into.rs | 1 + solutions/23_conversions/from_str.rs | 1 + solutions/23_conversions/try_from_into.rs | 1 + solutions/23_conversions/using_as.rs | 1 + solutions/quizzes/quiz1.rs | 1 + solutions/quizzes/quiz2.rs | 1 + solutions/quizzes/quiz3.rs | 1 + src/app_state.rs | 33 ++++--- src/embedded.rs | 110 +++++++++-------------- src/exercise.rs | 1 + src/init.rs | 4 +- 105 files changed, 199 insertions(+), 159 deletions(-) create mode 100644 solutions/00_intro/intro1.rs create mode 100644 solutions/00_intro/intro2.rs create mode 100644 solutions/01_variables/variables1.rs create mode 100644 solutions/01_variables/variables2.rs create mode 100644 solutions/01_variables/variables3.rs create mode 100644 solutions/01_variables/variables4.rs create mode 100644 solutions/01_variables/variables5.rs create mode 100644 solutions/01_variables/variables6.rs create mode 100644 solutions/02_functions/functions1.rs create mode 100644 solutions/02_functions/functions2.rs create mode 100644 solutions/02_functions/functions3.rs create mode 100644 solutions/02_functions/functions4.rs create mode 100644 solutions/02_functions/functions5.rs create mode 100644 solutions/03_if/if1.rs create mode 100644 solutions/03_if/if2.rs create mode 100644 solutions/03_if/if3.rs create mode 100644 solutions/04_primitive_types/primitive_types1.rs create mode 100644 solutions/04_primitive_types/primitive_types2.rs create mode 100644 solutions/04_primitive_types/primitive_types3.rs create mode 100644 solutions/04_primitive_types/primitive_types4.rs create mode 100644 solutions/04_primitive_types/primitive_types5.rs create mode 100644 solutions/04_primitive_types/primitive_types6.rs create mode 100644 solutions/05_vecs/vecs1.rs create mode 100644 solutions/05_vecs/vecs2.rs create mode 100644 solutions/06_move_semantics/move_semantics1.rs create mode 100644 solutions/06_move_semantics/move_semantics2.rs create mode 100644 solutions/06_move_semantics/move_semantics3.rs create mode 100644 solutions/06_move_semantics/move_semantics4.rs create mode 100644 solutions/06_move_semantics/move_semantics5.rs create mode 100644 solutions/06_move_semantics/move_semantics6.rs create mode 100644 solutions/07_structs/structs1.rs create mode 100644 solutions/07_structs/structs2.rs create mode 100644 solutions/07_structs/structs3.rs create mode 100644 solutions/08_enums/enums1.rs create mode 100644 solutions/08_enums/enums2.rs create mode 100644 solutions/08_enums/enums3.rs create mode 100644 solutions/09_strings/strings1.rs create mode 100644 solutions/09_strings/strings2.rs create mode 100644 solutions/09_strings/strings3.rs create mode 100644 solutions/09_strings/strings4.rs create mode 100644 solutions/10_modules/modules1.rs create mode 100644 solutions/10_modules/modules2.rs create mode 100644 solutions/10_modules/modules3.rs create mode 100644 solutions/11_hashmaps/hashmaps1.rs create mode 100644 solutions/11_hashmaps/hashmaps2.rs create mode 100644 solutions/11_hashmaps/hashmaps3.rs create mode 100644 solutions/12_options/options1.rs create mode 100644 solutions/12_options/options2.rs create mode 100644 solutions/12_options/options3.rs create mode 100644 solutions/13_error_handling/errors1.rs create mode 100644 solutions/13_error_handling/errors2.rs create mode 100644 solutions/13_error_handling/errors3.rs create mode 100644 solutions/13_error_handling/errors4.rs create mode 100644 solutions/13_error_handling/errors5.rs create mode 100644 solutions/13_error_handling/errors6.rs create mode 100644 solutions/14_generics/generics1.rs create mode 100644 solutions/14_generics/generics2.rs create mode 100644 solutions/15_traits/traits1.rs create mode 100644 solutions/15_traits/traits2.rs create mode 100644 solutions/15_traits/traits3.rs create mode 100644 solutions/15_traits/traits4.rs create mode 100644 solutions/15_traits/traits5.rs create mode 100644 solutions/16_lifetimes/lifetimes1.rs create mode 100644 solutions/16_lifetimes/lifetimes2.rs create mode 100644 solutions/16_lifetimes/lifetimes3.rs create mode 100644 solutions/17_tests/tests1.rs create mode 100644 solutions/17_tests/tests2.rs create mode 100644 solutions/17_tests/tests3.rs create mode 100644 solutions/17_tests/tests4.rs create mode 100644 solutions/18_iterators/iterators1.rs create mode 100644 solutions/18_iterators/iterators2.rs create mode 100644 solutions/18_iterators/iterators3.rs create mode 100644 solutions/18_iterators/iterators4.rs create mode 100644 solutions/18_iterators/iterators5.rs create mode 100644 solutions/19_smart_pointers/arc1.rs create mode 100644 solutions/19_smart_pointers/box1.rs create mode 100644 solutions/19_smart_pointers/cow1.rs create mode 100644 solutions/19_smart_pointers/rc1.rs create mode 100644 solutions/20_threads/threads1.rs create mode 100644 solutions/20_threads/threads2.rs create mode 100644 solutions/20_threads/threads3.rs create mode 100644 solutions/21_macros/macros1.rs create mode 100644 solutions/21_macros/macros2.rs create mode 100644 solutions/21_macros/macros3.rs create mode 100644 solutions/21_macros/macros4.rs create mode 100644 solutions/22_clippy/clippy1.rs create mode 100644 solutions/22_clippy/clippy2.rs create mode 100644 solutions/22_clippy/clippy3.rs create mode 100644 solutions/23_conversions/as_ref_mut.rs create mode 100644 solutions/23_conversions/from_into.rs create mode 100644 solutions/23_conversions/from_str.rs create mode 100644 solutions/23_conversions/try_from_into.rs create mode 100644 solutions/23_conversions/using_as.rs create mode 100644 solutions/quizzes/quiz1.rs create mode 100644 solutions/quizzes/quiz2.rs create mode 100644 solutions/quizzes/quiz3.rs (limited to 'Cargo.lock') diff --git a/Cargo.lock b/Cargo.lock index 1618eec..93617fa 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -690,6 +690,8 @@ name = "rustlings-macros" version = "6.0.0-alpha.0" dependencies = [ "quote", + "serde", + "toml_edit", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 6181b1e..1e6a24c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,6 +17,10 @@ authors = [ license = "MIT" edition = "2021" +[workspace.dependencies] +serde = { version = "1.0.198", features = ["derive"] } +toml_edit = { version = "0.22.12", default-features = false, features = ["parse", "serde"] } + [package] name = "rustlings" description = "Small exercises to get you used to reading and writing Rust code!" @@ -41,8 +45,8 @@ hashbrown = "0.14.3" notify-debouncer-mini = "0.4.1" ratatui = "0.26.2" rustlings-macros = { path = "rustlings-macros", version = "6.0.0-alpha.0" } -serde = { version = "1.0.198", features = ["derive"] } -toml_edit = { version = "0.22.12", default-features = false, features = ["parse", "serde"] } +serde.workspace = true +toml_edit.workspace = true which = "6.0.1" [dev-dependencies] diff --git a/info.toml b/info.toml index 27071a5..6549e1c 100644 --- a/info.toml +++ b/info.toml @@ -236,6 +236,7 @@ Make sure the type is consistent across all arms.""" [[exercises]] name = "quiz1" +dir = "quizzes" mode = "test" hint = "No hints this time ;)" @@ -637,6 +638,7 @@ Learn more at https://doc.rust-lang.org/book/ch08-03-hash-maps.html#updating-a-v [[exercises]] name = "quiz2" +dir = "quizzes" mode = "test" hint = "No hints this time ;)" @@ -870,6 +872,7 @@ See the documentation at: https://doc.rust-lang.org/book/ch10-02-traits.html#spe [[exercises]] name = "quiz3" +dir = "quizzes" mode = "test" hint = """ To find the best solution to this challenge you're going to need to think back diff --git a/rustlings-macros/Cargo.toml b/rustlings-macros/Cargo.toml index 095deaa..f925f69 100644 --- a/rustlings-macros/Cargo.toml +++ b/rustlings-macros/Cargo.toml @@ -11,3 +11,5 @@ proc-macro = true [dependencies] quote = "1.0.36" +serde.workspace = true +toml_edit.workspace = true diff --git a/rustlings-macros/src/lib.rs b/rustlings-macros/src/lib.rs index d95a93a..0bf3dbd 100644 --- a/rustlings-macros/src/lib.rs +++ b/rustlings-macros/src/lib.rs @@ -1,86 +1,39 @@ use proc_macro::TokenStream; use quote::quote; -use std::{fs::read_dir, panic, path::PathBuf}; +use serde::Deserialize; -fn path_to_string(path: PathBuf) -> String { - path.into_os_string() - .into_string() - .unwrap_or_else(|original| { - panic!("The path {} is invalid UTF8", original.to_string_lossy()); - }) +#[derive(Deserialize)] +struct ExerciseInfo { + name: String, + dir: String, +} + +#[derive(Deserialize)] +struct InfoFile { + exercises: Vec, } #[proc_macro] pub fn include_files(_: TokenStream) -> TokenStream { - let mut files = Vec::with_capacity(8); - let mut dirs = Vec::with_capacity(128); - - for entry in read_dir("exercises").expect("Failed to open the `exercises` directory") { - let entry = entry.expect("Failed to read the `exercises` directory"); - - if entry.file_type().unwrap().is_file() { - let path = entry.path(); - if path.file_name().unwrap() != "README.md" { - files.push(path_to_string(path)); - } - - continue; - } - - let dir_path = entry.path(); - let dir_files = read_dir(&dir_path).unwrap_or_else(|e| { - panic!("Failed to open the directory {}: {e}", dir_path.display()); - }); - let dir_path = path_to_string(dir_path); - let dir_files = dir_files.filter_map(|entry| { - let entry = entry.unwrap_or_else(|e| { - panic!("Failed to read the directory {dir_path}: {e}"); - }); - let path = entry.path(); - - if !entry.file_type().unwrap().is_file() { - panic!("Found {} but expected only files", path.display()); - } - - if path.file_name().unwrap() == "README.md" { - return None; - } - - Some(path_to_string(path)) - }); - - dirs.push(quote! { - EmbeddedFlatDir { - path: #dir_path, - readme: EmbeddedFile { - path: ::std::concat!(#dir_path, "/README.md"), - content: ::std::include_bytes!(::std::concat!("../", #dir_path, "/README.md")), - }, - content: &[ - #(EmbeddedFile { - path: #dir_files, - content: ::std::include_bytes!(::std::concat!("../", #dir_files)), - }),* - ], - } - }); - } + let exercises = toml_edit::de::from_str::(include_str!("../../info.toml")) + .expect("Failed to parse `info.toml`") + .exercises; + + let exercise_files = exercises + .iter() + .map(|exercise| format!("../exercises/{}/{}.rs", exercise.dir, exercise.name)); + let solution_files = exercises + .iter() + .map(|exercise| format!("../solutions/{}/{}.rs", exercise.dir, exercise.name)); + let dirs = exercises.iter().map(|exercise| &exercise.dir); + let readmes = exercises + .iter() + .map(|exercise| format!("../exercises/{}/README.md", exercise.dir)); quote! { EmbeddedFiles { - exercises_dir: ExercisesDir { - readme: EmbeddedFile { - path: "exercises/README.md", - content: ::std::include_bytes!("../exercises/README.md"), - }, - files: &[#( - EmbeddedFile { - path: #files, - content: ::std::include_bytes!(::std::concat!("../", #files)), - } - ),*], - dirs: &[#(#dirs),*], - }, + exercise_files: &[#(ExerciseFiles { exercise: include_bytes!(#exercise_files), solution: include_bytes!(#solution_files) }),*], + exercise_dirs: &[#(ExerciseDir { name: #dirs, readme: include_bytes!(#readmes) }),*] } } .into() diff --git a/solutions/00_intro/intro1.rs b/solutions/00_intro/intro1.rs new file mode 100644 index 0000000..70b786d --- /dev/null +++ b/solutions/00_intro/intro1.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/00_intro/intro2.rs b/solutions/00_intro/intro2.rs new file mode 100644 index 0000000..70b786d --- /dev/null +++ b/solutions/00_intro/intro2.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/01_variables/variables1.rs b/solutions/01_variables/variables1.rs new file mode 100644 index 0000000..70b786d --- /dev/null +++ b/solutions/01_variables/variables1.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/01_variables/variables2.rs b/solutions/01_variables/variables2.rs new file mode 100644 index 0000000..70b786d --- /dev/null +++ b/solutions/01_variables/variables2.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/01_variables/variables3.rs b/solutions/01_variables/variables3.rs new file mode 100644 index 0000000..70b786d --- /dev/null +++ b/solutions/01_variables/variables3.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/01_variables/variables4.rs b/solutions/01_variables/variables4.rs new file mode 100644 index 0000000..70b786d --- /dev/null +++ b/solutions/01_variables/variables4.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/01_variables/variables5.rs b/solutions/01_variables/variables5.rs new file mode 100644 index 0000000..70b786d --- /dev/null +++ b/solutions/01_variables/variables5.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/01_variables/variables6.rs b/solutions/01_variables/variables6.rs new file mode 100644 index 0000000..70b786d --- /dev/null +++ b/solutions/01_variables/variables6.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/02_functions/functions1.rs b/solutions/02_functions/functions1.rs new file mode 100644 index 0000000..70b786d --- /dev/null +++ b/solutions/02_functions/functions1.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/02_functions/functions2.rs b/solutions/02_functions/functions2.rs new file mode 100644 index 0000000..70b786d --- /dev/null +++ b/solutions/02_functions/functions2.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/02_functions/functions3.rs b/solutions/02_functions/functions3.rs new file mode 100644 index 0000000..70b786d --- /dev/null +++ b/solutions/02_functions/functions3.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/02_functions/functions4.rs b/solutions/02_functions/functions4.rs new file mode 100644 index 0000000..70b786d --- /dev/null +++ b/solutions/02_functions/functions4.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/02_functions/functions5.rs b/solutions/02_functions/functions5.rs new file mode 100644 index 0000000..70b786d --- /dev/null +++ b/solutions/02_functions/functions5.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/03_if/if1.rs b/solutions/03_if/if1.rs new file mode 100644 index 0000000..70b786d --- /dev/null +++ b/solutions/03_if/if1.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/03_if/if2.rs b/solutions/03_if/if2.rs new file mode 100644 index 0000000..70b786d --- /dev/null +++ b/solutions/03_if/if2.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/03_if/if3.rs b/solutions/03_if/if3.rs new file mode 100644 index 0000000..70b786d --- /dev/null +++ b/solutions/03_if/if3.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/04_primitive_types/primitive_types1.rs b/solutions/04_primitive_types/primitive_types1.rs new file mode 100644 index 0000000..70b786d --- /dev/null +++ b/solutions/04_primitive_types/primitive_types1.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/04_primitive_types/primitive_types2.rs b/solutions/04_primitive_types/primitive_types2.rs new file mode 100644 index 0000000..70b786d --- /dev/null +++ b/solutions/04_primitive_types/primitive_types2.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/04_primitive_types/primitive_types3.rs b/solutions/04_primitive_types/primitive_types3.rs new file mode 100644 index 0000000..70b786d --- /dev/null +++ b/solutions/04_primitive_types/primitive_types3.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/04_primitive_types/primitive_types4.rs b/solutions/04_primitive_types/primitive_types4.rs new file mode 100644 index 0000000..70b786d --- /dev/null +++ b/solutions/04_primitive_types/primitive_types4.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/04_primitive_types/primitive_types5.rs b/solutions/04_primitive_types/primitive_types5.rs new file mode 100644 index 0000000..70b786d --- /dev/null +++ b/solutions/04_primitive_types/primitive_types5.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/04_primitive_types/primitive_types6.rs b/solutions/04_primitive_types/primitive_types6.rs new file mode 100644 index 0000000..70b786d --- /dev/null +++ b/solutions/04_primitive_types/primitive_types6.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/05_vecs/vecs1.rs b/solutions/05_vecs/vecs1.rs new file mode 100644 index 0000000..70b786d --- /dev/null +++ b/solutions/05_vecs/vecs1.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/05_vecs/vecs2.rs b/solutions/05_vecs/vecs2.rs new file mode 100644 index 0000000..70b786d --- /dev/null +++ b/solutions/05_vecs/vecs2.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/06_move_semantics/move_semantics1.rs b/solutions/06_move_semantics/move_semantics1.rs new file mode 100644 index 0000000..70b786d --- /dev/null +++ b/solutions/06_move_semantics/move_semantics1.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/06_move_semantics/move_semantics2.rs b/solutions/06_move_semantics/move_semantics2.rs new file mode 100644 index 0000000..70b786d --- /dev/null +++ b/solutions/06_move_semantics/move_semantics2.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/06_move_semantics/move_semantics3.rs b/solutions/06_move_semantics/move_semantics3.rs new file mode 100644 index 0000000..70b786d --- /dev/null +++ b/solutions/06_move_semantics/move_semantics3.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/06_move_semantics/move_semantics4.rs b/solutions/06_move_semantics/move_semantics4.rs new file mode 100644 index 0000000..70b786d --- /dev/null +++ b/solutions/06_move_semantics/move_semantics4.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/06_move_semantics/move_semantics5.rs b/solutions/06_move_semantics/move_semantics5.rs new file mode 100644 index 0000000..70b786d --- /dev/null +++ b/solutions/06_move_semantics/move_semantics5.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/06_move_semantics/move_semantics6.rs b/solutions/06_move_semantics/move_semantics6.rs new file mode 100644 index 0000000..70b786d --- /dev/null +++ b/solutions/06_move_semantics/move_semantics6.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/07_structs/structs1.rs b/solutions/07_structs/structs1.rs new file mode 100644 index 0000000..70b786d --- /dev/null +++ b/solutions/07_structs/structs1.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/07_structs/structs2.rs b/solutions/07_structs/structs2.rs new file mode 100644 index 0000000..70b786d --- /dev/null +++ b/solutions/07_structs/structs2.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/07_structs/structs3.rs b/solutions/07_structs/structs3.rs new file mode 100644 index 0000000..70b786d --- /dev/null +++ b/solutions/07_structs/structs3.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/08_enums/enums1.rs b/solutions/08_enums/enums1.rs new file mode 100644 index 0000000..70b786d --- /dev/null +++ b/solutions/08_enums/enums1.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/08_enums/enums2.rs b/solutions/08_enums/enums2.rs new file mode 100644 index 0000000..70b786d --- /dev/null +++ b/solutions/08_enums/enums2.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/08_enums/enums3.rs b/solutions/08_enums/enums3.rs new file mode 100644 index 0000000..70b786d --- /dev/null +++ b/solutions/08_enums/enums3.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/09_strings/strings1.rs b/solutions/09_strings/strings1.rs new file mode 100644 index 0000000..70b786d --- /dev/null +++ b/solutions/09_strings/strings1.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/09_strings/strings2.rs b/solutions/09_strings/strings2.rs new file mode 100644 index 0000000..70b786d --- /dev/null +++ b/solutions/09_strings/strings2.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/09_strings/strings3.rs b/solutions/09_strings/strings3.rs new file mode 100644 index 0000000..70b786d --- /dev/null +++ b/solutions/09_strings/strings3.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/09_strings/strings4.rs b/solutions/09_strings/strings4.rs new file mode 100644 index 0000000..70b786d --- /dev/null +++ b/solutions/09_strings/strings4.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/10_modules/modules1.rs b/solutions/10_modules/modules1.rs new file mode 100644 index 0000000..70b786d --- /dev/null +++ b/solutions/10_modules/modules1.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/10_modules/modules2.rs b/solutions/10_modules/modules2.rs new file mode 100644 index 0000000..70b786d --- /dev/null +++ b/solutions/10_modules/modules2.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/10_modules/modules3.rs b/solutions/10_modules/modules3.rs new file mode 100644 index 0000000..70b786d --- /dev/null +++ b/solutions/10_modules/modules3.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/11_hashmaps/hashmaps1.rs b/solutions/11_hashmaps/hashmaps1.rs new file mode 100644 index 0000000..70b786d --- /dev/null +++ b/solutions/11_hashmaps/hashmaps1.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/11_hashmaps/hashmaps2.rs b/solutions/11_hashmaps/hashmaps2.rs new file mode 100644 index 0000000..70b786d --- /dev/null +++ b/solutions/11_hashmaps/hashmaps2.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/11_hashmaps/hashmaps3.rs b/solutions/11_hashmaps/hashmaps3.rs new file mode 100644 index 0000000..70b786d --- /dev/null +++ b/solutions/11_hashmaps/hashmaps3.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/12_options/options1.rs b/solutions/12_options/options1.rs new file mode 100644 index 0000000..70b786d --- /dev/null +++ b/solutions/12_options/options1.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/12_options/options2.rs b/solutions/12_options/options2.rs new file mode 100644 index 0000000..70b786d --- /dev/null +++ b/solutions/12_options/options2.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/12_options/options3.rs b/solutions/12_options/options3.rs new file mode 100644 index 0000000..70b786d --- /dev/null +++ b/solutions/12_options/options3.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/13_error_handling/errors1.rs b/solutions/13_error_handling/errors1.rs new file mode 100644 index 0000000..70b786d --- /dev/null +++ b/solutions/13_error_handling/errors1.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/13_error_handling/errors2.rs b/solutions/13_error_handling/errors2.rs new file mode 100644 index 0000000..70b786d --- /dev/null +++ b/solutions/13_error_handling/errors2.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/13_error_handling/errors3.rs b/solutions/13_error_handling/errors3.rs new file mode 100644 index 0000000..70b786d --- /dev/null +++ b/solutions/13_error_handling/errors3.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/13_error_handling/errors4.rs b/solutions/13_error_handling/errors4.rs new file mode 100644 index 0000000..70b786d --- /dev/null +++ b/solutions/13_error_handling/errors4.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/13_error_handling/errors5.rs b/solutions/13_error_handling/errors5.rs new file mode 100644 index 0000000..70b786d --- /dev/null +++ b/solutions/13_error_handling/errors5.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/13_error_handling/errors6.rs b/solutions/13_error_handling/errors6.rs new file mode 100644 index 0000000..70b786d --- /dev/null +++ b/solutions/13_error_handling/errors6.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/14_generics/generics1.rs b/solutions/14_generics/generics1.rs new file mode 100644 index 0000000..70b786d --- /dev/null +++ b/solutions/14_generics/generics1.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/14_generics/generics2.rs b/solutions/14_generics/generics2.rs new file mode 100644 index 0000000..70b786d --- /dev/null +++ b/solutions/14_generics/generics2.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/15_traits/traits1.rs b/solutions/15_traits/traits1.rs new file mode 100644 index 0000000..70b786d --- /dev/null +++ b/solutions/15_traits/traits1.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/15_traits/traits2.rs b/solutions/15_traits/traits2.rs new file mode 100644 index 0000000..70b786d --- /dev/null +++ b/solutions/15_traits/traits2.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/15_traits/traits3.rs b/solutions/15_traits/traits3.rs new file mode 100644 index 0000000..70b786d --- /dev/null +++ b/solutions/15_traits/traits3.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/15_traits/traits4.rs b/solutions/15_traits/traits4.rs new file mode 100644 index 0000000..70b786d --- /dev/null +++ b/solutions/15_traits/traits4.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/15_traits/traits5.rs b/solutions/15_traits/traits5.rs new file mode 100644 index 0000000..70b786d --- /dev/null +++ b/solutions/15_traits/traits5.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/16_lifetimes/lifetimes1.rs b/solutions/16_lifetimes/lifetimes1.rs new file mode 100644 index 0000000..70b786d --- /dev/null +++ b/solutions/16_lifetimes/lifetimes1.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/16_lifetimes/lifetimes2.rs b/solutions/16_lifetimes/lifetimes2.rs new file mode 100644 index 0000000..70b786d --- /dev/null +++ b/solutions/16_lifetimes/lifetimes2.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/16_lifetimes/lifetimes3.rs b/solutions/16_lifetimes/lifetimes3.rs new file mode 100644 index 0000000..70b786d --- /dev/null +++ b/solutions/16_lifetimes/lifetimes3.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/17_tests/tests1.rs b/solutions/17_tests/tests1.rs new file mode 100644 index 0000000..70b786d --- /dev/null +++ b/solutions/17_tests/tests1.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/17_tests/tests2.rs b/solutions/17_tests/tests2.rs new file mode 100644 index 0000000..70b786d --- /dev/null +++ b/solutions/17_tests/tests2.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/17_tests/tests3.rs b/solutions/17_tests/tests3.rs new file mode 100644 index 0000000..70b786d --- /dev/null +++ b/solutions/17_tests/tests3.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/17_tests/tests4.rs b/solutions/17_tests/tests4.rs new file mode 100644 index 0000000..70b786d --- /dev/null +++ b/solutions/17_tests/tests4.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/18_iterators/iterators1.rs b/solutions/18_iterators/iterators1.rs new file mode 100644 index 0000000..70b786d --- /dev/null +++ b/solutions/18_iterators/iterators1.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/18_iterators/iterators2.rs b/solutions/18_iterators/iterators2.rs new file mode 100644 index 0000000..70b786d --- /dev/null +++ b/solutions/18_iterators/iterators2.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/18_iterators/iterators3.rs b/solutions/18_iterators/iterators3.rs new file mode 100644 index 0000000..70b786d --- /dev/null +++ b/solutions/18_iterators/iterators3.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/18_iterators/iterators4.rs b/solutions/18_iterators/iterators4.rs new file mode 100644 index 0000000..70b786d --- /dev/null +++ b/solutions/18_iterators/iterators4.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/18_iterators/iterators5.rs b/solutions/18_iterators/iterators5.rs new file mode 100644 index 0000000..70b786d --- /dev/null +++ b/solutions/18_iterators/iterators5.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/19_smart_pointers/arc1.rs b/solutions/19_smart_pointers/arc1.rs new file mode 100644 index 0000000..70b786d --- /dev/null +++ b/solutions/19_smart_pointers/arc1.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/19_smart_pointers/box1.rs b/solutions/19_smart_pointers/box1.rs new file mode 100644 index 0000000..70b786d --- /dev/null +++ b/solutions/19_smart_pointers/box1.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/19_smart_pointers/cow1.rs b/solutions/19_smart_pointers/cow1.rs new file mode 100644 index 0000000..70b786d --- /dev/null +++ b/solutions/19_smart_pointers/cow1.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/19_smart_pointers/rc1.rs b/solutions/19_smart_pointers/rc1.rs new file mode 100644 index 0000000..70b786d --- /dev/null +++ b/solutions/19_smart_pointers/rc1.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/20_threads/threads1.rs b/solutions/20_threads/threads1.rs new file mode 100644 index 0000000..70b786d --- /dev/null +++ b/solutions/20_threads/threads1.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/20_threads/threads2.rs b/solutions/20_threads/threads2.rs new file mode 100644 index 0000000..70b786d --- /dev/null +++ b/solutions/20_threads/threads2.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/20_threads/threads3.rs b/solutions/20_threads/threads3.rs new file mode 100644 index 0000000..70b786d --- /dev/null +++ b/solutions/20_threads/threads3.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/21_macros/macros1.rs b/solutions/21_macros/macros1.rs new file mode 100644 index 0000000..70b786d --- /dev/null +++ b/solutions/21_macros/macros1.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/21_macros/macros2.rs b/solutions/21_macros/macros2.rs new file mode 100644 index 0000000..70b786d --- /dev/null +++ b/solutions/21_macros/macros2.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/21_macros/macros3.rs b/solutions/21_macros/macros3.rs new file mode 100644 index 0000000..70b786d --- /dev/null +++ b/solutions/21_macros/macros3.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/21_macros/macros4.rs b/solutions/21_macros/macros4.rs new file mode 100644 index 0000000..70b786d --- /dev/null +++ b/solutions/21_macros/macros4.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/22_clippy/clippy1.rs b/solutions/22_clippy/clippy1.rs new file mode 100644 index 0000000..70b786d --- /dev/null +++ b/solutions/22_clippy/clippy1.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/22_clippy/clippy2.rs b/solutions/22_clippy/clippy2.rs new file mode 100644 index 0000000..70b786d --- /dev/null +++ b/solutions/22_clippy/clippy2.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/22_clippy/clippy3.rs b/solutions/22_clippy/clippy3.rs new file mode 100644 index 0000000..70b786d --- /dev/null +++ b/solutions/22_clippy/clippy3.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/23_conversions/as_ref_mut.rs b/solutions/23_conversions/as_ref_mut.rs new file mode 100644 index 0000000..70b786d --- /dev/null +++ b/solutions/23_conversions/as_ref_mut.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/23_conversions/from_into.rs b/solutions/23_conversions/from_into.rs new file mode 100644 index 0000000..70b786d --- /dev/null +++ b/solutions/23_conversions/from_into.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/23_conversions/from_str.rs b/solutions/23_conversions/from_str.rs new file mode 100644 index 0000000..70b786d --- /dev/null +++ b/solutions/23_conversions/from_str.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/23_conversions/try_from_into.rs b/solutions/23_conversions/try_from_into.rs new file mode 100644 index 0000000..70b786d --- /dev/null +++ b/solutions/23_conversions/try_from_into.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/23_conversions/using_as.rs b/solutions/23_conversions/using_as.rs new file mode 100644 index 0000000..70b786d --- /dev/null +++ b/solutions/23_conversions/using_as.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/quizzes/quiz1.rs b/solutions/quizzes/quiz1.rs new file mode 100644 index 0000000..70b786d --- /dev/null +++ b/solutions/quizzes/quiz1.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/quizzes/quiz2.rs b/solutions/quizzes/quiz2.rs new file mode 100644 index 0000000..70b786d --- /dev/null +++ b/solutions/quizzes/quiz2.rs @@ -0,0 +1 @@ +// TODO diff --git a/solutions/quizzes/quiz3.rs b/solutions/quizzes/quiz3.rs new file mode 100644 index 0000000..70b786d --- /dev/null +++ b/solutions/quizzes/quiz3.rs @@ -0,0 +1 @@ +// TODO diff --git a/src/app_state.rs b/src/app_state.rs index 09de2a3..0767c2b 100644 --- a/src/app_state.rs +++ b/src/app_state.rs @@ -11,11 +11,7 @@ use std::{ process::{Command, Stdio}, }; -use crate::{ - embedded::{WriteStrategy, EMBEDDED_FILES}, - exercise::Exercise, - info_file::ExerciseInfo, -}; +use crate::{embedded::EMBEDDED_FILES, exercise::Exercise, info_file::ExerciseInfo}; const STATE_FILE_NAME: &str = ".rustlings-state.txt"; const BAD_INDEX_ERR: &str = "The current exercise index is higher than the number of exercises"; @@ -100,10 +96,15 @@ impl AppState { exercise_info.name.shrink_to_fit(); let name = exercise_info.name.leak(); + let dir = exercise_info.dir.map(|mut dir| { + dir.shrink_to_fit(); + &*dir.leak() + }); let hint = exercise_info.hint.trim().to_owned(); Exercise { + dir, name, path, mode: exercise_info.mode, @@ -181,10 +182,16 @@ impl AppState { Ok(()) } - fn reset_path(&self, path: &str) -> Result<()> { + fn reset(&self, ind: usize, dir_name: Option<&str>, path: &str) -> Result<()> { if self.official_exercises { return EMBEDDED_FILES - .write_exercise_to_disk(path, WriteStrategy::Overwrite) + .write_exercise_to_disk( + ind, + dir_name.context( + "Official exercises must be nested in the `exercises` directory", + )?, + path, + ) .with_context(|| format!("Failed to reset the exercise {path}")); } @@ -209,11 +216,11 @@ impl AppState { } pub fn reset_current_exercise(&mut self) -> Result<&'static str> { - let path = self.current_exercise().path; self.set_pending(self.current_exercise_ind)?; - self.reset_path(path)?; + let exercise = self.current_exercise(); + self.reset(self.current_exercise_ind, exercise.dir, exercise.path)?; - Ok(path) + Ok(exercise.path) } pub fn reset_exercise_by_ind(&mut self, exercise_ind: usize) -> Result<&'static str> { @@ -221,11 +228,11 @@ impl AppState { bail!(BAD_INDEX_ERR); } - let path = self.exercises[exercise_ind].path; self.set_pending(exercise_ind)?; - self.reset_path(path)?; + let exercise = &self.exercises[exercise_ind]; + self.reset(exercise_ind, exercise.dir, exercise.path)?; - Ok(path) + Ok(exercise.path) } fn next_pending_exercise_ind(&self) -> Option { diff --git a/src/embedded.rs b/src/embedded.rs index eae3099..ccd8dca 100644 --- a/src/embedded.rs +++ b/src/embedded.rs @@ -1,9 +1,10 @@ use std::{ - fs::{create_dir, File, OpenOptions}, + fs::{create_dir, OpenOptions}, io::{self, Write}, - path::Path, }; +use crate::info_file::ExerciseInfo; + pub static EMBEDDED_FILES: EmbeddedFiles = rustlings_macros::include_files!(); #[derive(Clone, Copy)] @@ -13,107 +14,78 @@ pub enum WriteStrategy { } impl WriteStrategy { - fn open>(self, path: P) -> io::Result { - match self { + fn write(self, path: &str, content: &[u8]) -> io::Result<()> { + let file = match self { Self::IfNotExists => OpenOptions::new().create_new(true).write(true).open(path), Self::Overwrite => OpenOptions::new() .create(true) .write(true) .truncate(true) .open(path), - } + }; + + file?.write_all(content) } } -struct EmbeddedFile { - path: &'static str, - content: &'static [u8], +struct ExerciseFiles { + exercise: &'static [u8], + solution: &'static [u8], } -impl EmbeddedFile { - fn write_to_disk(&self, strategy: WriteStrategy) -> io::Result<()> { - strategy.open(self.path)?.write_all(self.content) - } +struct ExerciseDir { + name: &'static str, + readme: &'static [u8], } -struct EmbeddedFlatDir { - path: &'static str, - readme: EmbeddedFile, - content: &'static [EmbeddedFile], -} - -impl EmbeddedFlatDir { +impl ExerciseDir { fn init_on_disk(&self) -> io::Result<()> { - let path = Path::new(self.path); - - if let Err(e) = create_dir(path) { - if e.kind() != io::ErrorKind::AlreadyExists { - return Err(e); + if let Err(e) = create_dir(format!("exercises/{}", self.name)) { + if e.kind() == io::ErrorKind::AlreadyExists { + return Ok(()); } + + return Err(e); } - self.readme.write_to_disk(WriteStrategy::Overwrite) + WriteStrategy::Overwrite.write(&format!("exercises/{}/README.md", self.name), self.readme) } } -struct ExercisesDir { - readme: EmbeddedFile, - files: &'static [EmbeddedFile], - dirs: &'static [EmbeddedFlatDir], -} - pub struct EmbeddedFiles { - exercises_dir: ExercisesDir, + exercise_files: &'static [ExerciseFiles], + exercise_dirs: &'static [ExerciseDir], } impl EmbeddedFiles { - pub fn init_exercises_dir(&self) -> io::Result<()> { + pub fn init_exercises_dir(&self, exercise_infos: &[ExerciseInfo]) -> io::Result<()> { create_dir("exercises")?; - self.exercises_dir - .readme - .write_to_disk(WriteStrategy::IfNotExists)?; - - for file in self.exercises_dir.files { - file.write_to_disk(WriteStrategy::IfNotExists)?; - } + WriteStrategy::IfNotExists.write( + "exercises/README.md", + include_bytes!("../exercises/README.md"), + )?; - for dir in self.exercises_dir.dirs { + for dir in self.exercise_dirs { dir.init_on_disk()?; + } - for file in dir.content { - file.write_to_disk(WriteStrategy::IfNotExists)?; - } + for (exercise_info, exercise_files) in exercise_infos.iter().zip(self.exercise_files) { + WriteStrategy::IfNotExists.write(&exercise_info.path(), exercise_files.exercise)?; } Ok(()) } - pub fn write_exercise_to_disk

(&self, path: P, strategy: WriteStrategy) -> io::Result<()> - where - P: AsRef, - { - let path = path.as_ref(); - - if let Some(file) = self - .exercises_dir - .files - .iter() - .find(|file| Path::new(file.path) == path) - { - return file.write_to_disk(strategy); - } - - for dir in self.exercises_dir.dirs { - if let Some(file) = dir.content.iter().find(|file| Path::new(file.path) == path) { - dir.init_on_disk()?; - return file.write_to_disk(strategy); - } - } + pub fn write_exercise_to_disk(&self, exercise_ind: usize, dir_name: &str, path: &str) -> io::Result<()> { + let Some(dir) = self.exercise_dirs.iter().find(|dir| dir.name == dir_name) else { + return Err(io::Error::new( + io::ErrorKind::NotFound, + format!("`{dir_name}` not found in the embedded directories"), + )); + }; - Err(io::Error::new( - io::ErrorKind::NotFound, - format!("{} not found in the embedded files", path.display()), - )) + dir.init_on_disk()?; + WriteStrategy::Overwrite.write(path, self.exercise_files[exercise_ind].exercise) } } diff --git a/src/exercise.rs b/src/exercise.rs index e85efe4..4493a57 100644 --- a/src/exercise.rs +++ b/src/exercise.rs @@ -31,6 +31,7 @@ impl<'a> Display for TerminalFileLink<'a> { } pub struct Exercise { + pub dir: Option<&'static str>, // Exercise's unique name pub name: &'static str, // Exercise's path diff --git a/src/init.rs b/src/init.rs index f210db7..f1a9509 100644 --- a/src/init.rs +++ b/src/init.rs @@ -24,11 +24,11 @@ pub fn init() -> Result<()> { set_current_dir("rustlings") .context("Failed to change the current directory to `rustlings`")?; + let info_file = InfoFile::parse()?; EMBEDDED_FILES - .init_exercises_dir() + .init_exercises_dir(&info_file.exercises) .context("Failed to initialize the `rustlings/exercises` directory")?; - let info_file = InfoFile::parse()?; let current_cargo_toml = include_str!("../dev/Cargo.toml"); // Skip the first line (comment). let newline_ind = current_cargo_toml -- cgit v1.2.3 From 4ef345e706ab5150d96faadf41a28aee49534361 Mon Sep 17 00:00:00 2001 From: mo8it Date: Wed, 24 Apr 2024 15:58:34 +0200 Subject: Update dependency --- Cargo.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'Cargo.lock') diff --git a/Cargo.lock b/Cargo.lock index 93617fa..df9ee74 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -954,11 +954,11 @@ checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-util" -version = "0.1.6" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596" +checksum = "134306a13c5647ad6453e8deaec55d3a44d6021970129e6188735e74bf546697" dependencies = [ - "winapi", + "windows-sys 0.52.0", ] [[package]] -- cgit v1.2.3 From 0df0be835279a9dd176244b9014f459399153300 Mon Sep 17 00:00:00 2001 From: mo8it Date: Wed, 24 Apr 2024 16:26:34 +0200 Subject: Update Cargo.lock --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'Cargo.lock') diff --git a/Cargo.lock b/Cargo.lock index df9ee74..5f0b597 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -669,7 +669,7 @@ dependencies = [ [[package]] name = "rustlings" -version = "6.0.0-alpha.0" +version = "6.0.0-beta.0" dependencies = [ "anyhow", "assert_cmd", @@ -687,7 +687,7 @@ dependencies = [ [[package]] name = "rustlings-macros" -version = "6.0.0-alpha.0" +version = "6.0.0-beta.0" dependencies = [ "quote", "serde", -- cgit v1.2.3 From 67fa01774223b08833c21baeb13bdec9e4a298a0 Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 25 Apr 2024 01:56:01 +0200 Subject: Use os_pipe --- Cargo.lock | 11 ++++++ Cargo.toml | 1 + src/app_state.rs | 13 ++++++- src/exercise.rs | 110 ++++++++++++++++++++++++++++++++++++++++++++++------- src/main.rs | 6 ++- src/run.rs | 11 +++--- src/watch/state.rs | 26 +++---------- 7 files changed, 135 insertions(+), 43 deletions(-) (limited to 'Cargo.lock') diff --git a/Cargo.lock b/Cargo.lock index 5f0b597..823fec3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -519,6 +519,16 @@ version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" +[[package]] +name = "os_pipe" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57119c3b893986491ec9aa85056780d3a0f3cf4da7cc09dd3650dbd6c6738fb9" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + [[package]] name = "parking_lot" version = "0.12.1" @@ -677,6 +687,7 @@ dependencies = [ "crossterm", "hashbrown", "notify-debouncer-mini", + "os_pipe", "predicates", "ratatui", "rustlings-macros", diff --git a/Cargo.toml b/Cargo.toml index f4615b1..31e7456 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -50,6 +50,7 @@ clap = { version = "4.5.4", features = ["derive"] } crossterm = "0.27.0" hashbrown = "0.14.3" notify-debouncer-mini = "0.4.1" +os_pipe = "1.1.5" ratatui = "0.26.2" rustlings-macros = { path = "rustlings-macros", version = "6.0.0-beta.0" } serde.workspace = true diff --git a/src/app_state.rs b/src/app_state.rs index 33d3de2..4160f6e 100644 --- a/src/app_state.rs +++ b/src/app_state.rs @@ -11,7 +11,12 @@ use std::{ process::{Command, Stdio}, }; -use crate::{embedded::EMBEDDED_FILES, exercise::Exercise, info_file::ExerciseInfo, DEBUG_PROFILE}; +use crate::{ + embedded::EMBEDDED_FILES, + exercise::{Exercise, OUTPUT_CAPACITY}, + info_file::ExerciseInfo, + DEBUG_PROFILE, +}; const STATE_FILE_NAME: &str = ".rustlings-state.txt"; const BAD_INDEX_ERR: &str = "The current exercise index is higher than the number of exercises"; @@ -302,11 +307,13 @@ impl AppState { let Some(ind) = self.next_pending_exercise_ind() else { writer.write_all(RERUNNING_ALL_EXERCISES_MSG)?; + let mut output = Vec::with_capacity(OUTPUT_CAPACITY); for (exercise_ind, exercise) in self.exercises().iter().enumerate() { writer.write_fmt(format_args!("Running {exercise} ... "))?; writer.flush()?; - if !exercise.run()?.status.success() { + let success = exercise.run(&mut output)?; + if !success { writer.write_fmt(format_args!("{}\n\n", "FAILED".red()))?; self.current_exercise_ind = exercise_ind; @@ -322,6 +329,8 @@ impl AppState { } writer.write_fmt(format_args!("{}\n", "ok".green()))?; + + output.clear(); } writer.execute(Clear(ClearType::All))?; diff --git a/src/exercise.rs b/src/exercise.rs index 45ac208..17cc8d7 100644 --- a/src/exercise.rs +++ b/src/exercise.rs @@ -2,11 +2,42 @@ use anyhow::{Context, Result}; use crossterm::style::{style, StyledContent, Stylize}; use std::{ fmt::{self, Display, Formatter}, - path::Path, - process::{Command, Output}, + io::{Read, Write}, + process::Command, }; -use crate::{info_file::Mode, terminal_link::TerminalFileLink, DEBUG_PROFILE}; +use crate::{in_official_repo, info_file::Mode, terminal_link::TerminalFileLink, DEBUG_PROFILE}; + +// TODO +pub const OUTPUT_CAPACITY: usize = 1 << 12; + +fn run_command(mut cmd: Command, cmd_description: &str, output: &mut Vec) -> Result { + let (mut reader, writer) = os_pipe::pipe().with_context(|| { + format!("Failed to create a pipe to run the command `{cmd_description}``") + })?; + + let mut handle = cmd + .stdout(writer.try_clone().with_context(|| { + format!("Failed to clone the pipe writer for the command `{cmd_description}`") + })?) + .stderr(writer) + .spawn() + .with_context(|| format!("Failed to run the command `{cmd_description}`"))?; + + // Prevent pipe deadlock. + drop(cmd); + + reader + .read_to_end(output) + .with_context(|| format!("Failed to read the output of the command `{cmd_description}`"))?; + + output.push(b'\n'); + + handle + .wait() + .with_context(|| format!("Failed to wait on the command `{cmd_description}` to exit")) + .map(|status| status.success()) +} pub struct Exercise { pub dir: Option<&'static str>, @@ -22,13 +53,30 @@ pub struct Exercise { } impl Exercise { - fn cargo_cmd(&self, command: &str, args: &[&str]) -> Result { + fn run_bin(&self, output: &mut Vec) -> Result { + writeln!(output, "{}", "Output".bold().magenta().underlined())?; + + let bin_path = format!("target/debug/{}", self.name); + run_command(Command::new(&bin_path), &bin_path, output) + } + + fn cargo_cmd( + &self, + command: &str, + args: &[&str], + cmd_description: &str, + output: &mut Vec, + dev: bool, + ) -> Result { let mut cmd = Command::new("cargo"); cmd.arg(command); // A hack to make `cargo run` work when developing Rustlings. - if DEBUG_PROFILE && Path::new("tests").exists() { - cmd.arg("--manifest-path").arg("dev/Cargo.toml"); + if dev { + cmd.arg("--manifest-path") + .arg("dev/Cargo.toml") + .arg("--target-dir") + .arg("target"); } cmd.arg("--color") @@ -36,15 +84,43 @@ impl Exercise { .arg("-q") .arg("--bin") .arg(self.name) - .args(args) - .output() - .context("Failed to run Cargo") + .args(args); + + run_command(cmd, cmd_description, output) + } + + fn cargo_cmd_with_bin_output( + &self, + command: &str, + args: &[&str], + cmd_description: &str, + output: &mut Vec, + dev: bool, + ) -> Result { + // Discard the output of `cargo build` because it will be shown again by the Cargo command. + output.clear(); + + let cargo_cmd_success = self.cargo_cmd(command, args, cmd_description, output, dev)?; + + let run_success = self.run_bin(output)?; + + Ok(cargo_cmd_success && run_success) } - pub fn run(&self) -> Result { + pub fn run(&self, output: &mut Vec) -> Result { + output.clear(); + + // Developing the official Rustlings. + let dev = DEBUG_PROFILE && in_official_repo(); + + let build_success = self.cargo_cmd("build", &[], "cargo build …", output, dev)?; + if !build_success { + return Ok(false); + } + match self.mode { - Mode::Run => self.cargo_cmd("run", &[]), - Mode::Test => self.cargo_cmd( + Mode::Run => self.run_bin(output), + Mode::Test => self.cargo_cmd_with_bin_output( "test", &[ "--", @@ -54,10 +130,16 @@ impl Exercise { "--format", "pretty", ], + "cargo test …", + output, + dev, ), - Mode::Clippy => self.cargo_cmd( + Mode::Clippy => self.cargo_cmd_with_bin_output( "clippy", - &["--", "-D", "warnings", "-D", "clippy::float_cmp"], + &["--", "-D", "warnings"], + "cargo clippy …", + output, + dev, ), } } diff --git a/src/main.rs b/src/main.rs index 790fff6..a928504 100644 --- a/src/main.rs +++ b/src/main.rs @@ -75,10 +75,14 @@ enum Subcommands { Dev(DevCommands), } +fn in_official_repo() -> bool { + Path::new("dev/rustlings-repo.txt").exists() +} + fn main() -> Result<()> { let args = Args::parse(); - if !DEBUG_PROFILE && Path::new("dev/rustlings-repo.txt").exists() { + if !DEBUG_PROFILE && in_official_repo() { bail!("{OLD_METHOD_ERR}"); } diff --git a/src/run.rs b/src/run.rs index a2b6972..1db8dcb 100644 --- a/src/run.rs +++ b/src/run.rs @@ -4,20 +4,19 @@ use std::io::{self, Write}; use crate::{ app_state::{AppState, ExercisesProgress}, + exercise::OUTPUT_CAPACITY, terminal_link::TerminalFileLink, }; pub fn run(app_state: &mut AppState) -> Result<()> { let exercise = app_state.current_exercise(); - let output = exercise.run()?; + let mut output = Vec::with_capacity(OUTPUT_CAPACITY); + let success = exercise.run(&mut output)?; let mut stdout = io::stdout().lock(); - stdout.write_all(&output.stdout)?; - stdout.write_all(b"\n")?; - stdout.write_all(&output.stderr)?; - stdout.flush()?; + stdout.write_all(&output)?; - if !output.status.success() { + if !success { app_state.set_pending(app_state.current_exercise_ind())?; bail!( diff --git a/src/watch/state.rs b/src/watch/state.rs index 5f4abf3..df492dc 100644 --- a/src/watch/state.rs +++ b/src/watch/state.rs @@ -8,6 +8,7 @@ use std::io::{self, StdoutLock, Write}; use crate::{ app_state::{AppState, ExercisesProgress}, + exercise::OUTPUT_CAPACITY, progress_bar::progress_bar, terminal_link::TerminalFileLink, }; @@ -21,8 +22,7 @@ enum DoneStatus { pub struct WatchState<'a> { writer: StdoutLock<'a>, app_state: &'a mut AppState, - stdout: Option>, - stderr: Option>, + output: Vec, show_hint: bool, done_status: DoneStatus, manual_run: bool, @@ -35,8 +35,7 @@ impl<'a> WatchState<'a> { Self { writer, app_state, - stdout: None, - stderr: None, + output: Vec::with_capacity(OUTPUT_CAPACITY), show_hint: false, done_status: DoneStatus::Pending, manual_run, @@ -51,11 +50,8 @@ impl<'a> WatchState<'a> { pub fn run_current_exercise(&mut self) -> Result<()> { self.show_hint = false; - let output = self.app_state.current_exercise().run()?; - self.stdout = Some(output.stdout); - - if output.status.success() { - self.stderr = None; + let success = self.app_state.current_exercise().run(&mut self.output)?; + if success { self.done_status = if let Some(solution_path) = self.app_state.current_solution_path()? { DoneStatus::DoneWithSolution(solution_path) @@ -66,7 +62,6 @@ impl<'a> WatchState<'a> { self.app_state .set_pending(self.app_state.current_exercise_ind())?; - self.stderr = Some(output.stderr); self.done_status = DoneStatus::Pending; } @@ -116,16 +111,7 @@ impl<'a> WatchState<'a> { self.writer.execute(Clear(ClearType::All))?; - if let Some(stdout) = &self.stdout { - self.writer.write_all(stdout)?; - self.writer.write_all(b"\n")?; - } - - if let Some(stderr) = &self.stderr { - self.writer.write_all(stderr)?; - self.writer.write_all(b"\n")?; - } - + self.writer.write_all(&self.output)?; self.writer.write_all(b"\n")?; if self.show_hint { -- cgit v1.2.3 From 29abaee4ec22627475aeabaf8425f759d2463530 Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 25 Apr 2024 15:22:14 +0200 Subject: Update dep --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'Cargo.lock') diff --git a/Cargo.lock b/Cargo.lock index 823fec3..23c4887 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -965,9 +965,9 @@ checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-util" -version = "0.1.7" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "134306a13c5647ad6453e8deaec55d3a44d6021970129e6188735e74bf546697" +checksum = "4d4cc384e1e73b93bafa6fb4f1df8c41695c8a91cf9c4c64358067d15a7b6c6b" dependencies = [ "windows-sys 0.52.0", ] -- cgit v1.2.3 From ca41f9e2df5512e9c283dfde9867a5329e9751c9 Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 25 Apr 2024 19:02:07 +0200 Subject: Prepare for using cargo-release --- .typos.toml | 7 +++++++ Cargo.lock | 4 ++-- Cargo.toml | 8 ++++++-- release-hook.sh | 8 ++++++++ rustlings-macros/Cargo.toml | 3 +++ 5 files changed, 26 insertions(+), 4 deletions(-) create mode 100644 .typos.toml create mode 100755 release-hook.sh (limited to 'Cargo.lock') diff --git a/.typos.toml b/.typos.toml new file mode 100644 index 0000000..a74498a --- /dev/null +++ b/.typos.toml @@ -0,0 +1,7 @@ +[files] +extend-exclude = [ + "CHANGELOG.md", +] + +[default.extend-words] +"ratatui" = "ratatui" diff --git a/Cargo.lock b/Cargo.lock index 23c4887..788cbbb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -679,7 +679,7 @@ dependencies = [ [[package]] name = "rustlings" -version = "6.0.0-beta.0" +version = "6.0.0-alpha.0" dependencies = [ "anyhow", "assert_cmd", @@ -698,7 +698,7 @@ dependencies = [ [[package]] name = "rustlings-macros" -version = "6.0.0-beta.0" +version = "6.0.0-alpha.0" dependencies = [ "quote", "serde", diff --git a/Cargo.toml b/Cargo.toml index 31e7456..5f22665 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,7 +8,7 @@ exclude = [ ] [workspace.package] -version = "6.0.0-beta.0" +version = "6.0.0-alpha.0" authors = [ "Liv ", "Mo Bitar ", @@ -41,6 +41,7 @@ include = [ "/info.toml", "/LICENSE", "/README.md", + "/solutions/", "/src/", ] @@ -52,7 +53,7 @@ hashbrown = "0.14.3" notify-debouncer-mini = "0.4.1" os_pipe = "1.1.5" ratatui = "0.26.2" -rustlings-macros = { path = "rustlings-macros", version = "6.0.0-beta.0" } +rustlings-macros = { path = "rustlings-macros", version = "=6.0.0-alpha.0" } serde.workspace = true toml_edit.workspace = true which = "6.0.1" @@ -66,3 +67,6 @@ panic = "abort" [profile.dev] panic = "abort" + +[package.metadata.release] +pre-release-hook = ["./release-hook.sh"] diff --git a/release-hook.sh b/release-hook.sh new file mode 100755 index 0000000..3a2c537 --- /dev/null +++ b/release-hook.sh @@ -0,0 +1,8 @@ +#!/bin/bash + +# Error out if any command fails +set -e + +typos +cargo outdated -w --exit-code 1 +cargo test --workspace --all-targets diff --git a/rustlings-macros/Cargo.toml b/rustlings-macros/Cargo.toml index c9c1d2f..f9aba66 100644 --- a/rustlings-macros/Cargo.toml +++ b/rustlings-macros/Cargo.toml @@ -14,3 +14,6 @@ proc-macro = true quote = "1.0.36" serde.workspace = true toml_edit.workspace = true + +[package.metadata.release] +verify = false -- cgit v1.2.3 From 5595e1c3975b71017b79771f3f0f85038e80d1e6 Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 25 Apr 2024 19:32:10 +0200 Subject: chore: Release --- Cargo.lock | 4 ++-- Cargo.toml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'Cargo.lock') diff --git a/Cargo.lock b/Cargo.lock index 788cbbb..ca4d131 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -679,7 +679,7 @@ dependencies = [ [[package]] name = "rustlings" -version = "6.0.0-alpha.0" +version = "6.0.0-beta.1" dependencies = [ "anyhow", "assert_cmd", @@ -698,7 +698,7 @@ dependencies = [ [[package]] name = "rustlings-macros" -version = "6.0.0-alpha.0" +version = "6.0.0-beta.1" dependencies = [ "quote", "serde", diff --git a/Cargo.toml b/Cargo.toml index 5f22665..caa139e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,7 +8,7 @@ exclude = [ ] [workspace.package] -version = "6.0.0-alpha.0" +version = "6.0.0-beta.1" authors = [ "Liv ", "Mo Bitar ", @@ -53,7 +53,7 @@ hashbrown = "0.14.3" notify-debouncer-mini = "0.4.1" os_pipe = "1.1.5" ratatui = "0.26.2" -rustlings-macros = { path = "rustlings-macros", version = "=6.0.0-alpha.0" } +rustlings-macros = { path = "rustlings-macros", version = "=6.0.0-beta.1" } serde.workspace = true toml_edit.workspace = true which = "6.0.1" -- cgit v1.2.3 From aaea5b490fabecbd1111a6ece6446fedfc9800f3 Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 25 Apr 2024 19:54:59 +0200 Subject: chore: Release --- Cargo.lock | 4 ++-- Cargo.toml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'Cargo.lock') diff --git a/Cargo.lock b/Cargo.lock index ca4d131..6faeea2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -679,7 +679,7 @@ dependencies = [ [[package]] name = "rustlings" -version = "6.0.0-beta.1" +version = "6.0.0-beta.2" dependencies = [ "anyhow", "assert_cmd", @@ -698,7 +698,7 @@ dependencies = [ [[package]] name = "rustlings-macros" -version = "6.0.0-beta.1" +version = "6.0.0-beta.2" dependencies = [ "quote", "serde", diff --git a/Cargo.toml b/Cargo.toml index caa139e..78ebb9c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,7 +8,7 @@ exclude = [ ] [workspace.package] -version = "6.0.0-beta.1" +version = "6.0.0-beta.2" authors = [ "Liv ", "Mo Bitar ", @@ -53,7 +53,7 @@ hashbrown = "0.14.3" notify-debouncer-mini = "0.4.1" os_pipe = "1.1.5" ratatui = "0.26.2" -rustlings-macros = { path = "rustlings-macros", version = "=6.0.0-beta.1" } +rustlings-macros = { path = "rustlings-macros", version = "=6.0.0-beta.2" } serde.workspace = true toml_edit.workspace = true which = "6.0.1" -- cgit v1.2.3 From 0d7b0361375fb73f537d672bbecc11181e19e8c9 Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 25 Apr 2024 21:07:41 +0200 Subject: chore: Release --- Cargo.lock | 4 ++-- Cargo.toml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'Cargo.lock') diff --git a/Cargo.lock b/Cargo.lock index 6faeea2..5767267 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -679,7 +679,7 @@ dependencies = [ [[package]] name = "rustlings" -version = "6.0.0-beta.2" +version = "6.0.0-beta.3" dependencies = [ "anyhow", "assert_cmd", @@ -698,7 +698,7 @@ dependencies = [ [[package]] name = "rustlings-macros" -version = "6.0.0-beta.2" +version = "6.0.0-beta.3" dependencies = [ "quote", "serde", diff --git a/Cargo.toml b/Cargo.toml index b3bdaed..1bc26c9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,7 +8,7 @@ exclude = [ ] [workspace.package] -version = "6.0.0-beta.2" +version = "6.0.0-beta.3" authors = [ "Liv ", "Mo Bitar ", @@ -55,7 +55,7 @@ hashbrown = "0.14.3" notify-debouncer-mini = "0.4.1" os_pipe = "1.1.5" ratatui = "0.26.2" -rustlings-macros = { path = "rustlings-macros", version = "=6.0.0-beta.2" } +rustlings-macros = { path = "rustlings-macros", version = "=6.0.0-beta.3" } serde.workspace = true toml_edit.workspace = true which = "6.0.1" -- cgit v1.2.3 From c82c3673245ca11d455b067c97fadda4a8406cb9 Mon Sep 17 00:00:00 2001 From: mo8it Date: Sat, 27 Apr 2024 04:14:59 +0200 Subject: Respect the target-dir config and show tests' output --- Cargo.lock | 69 +++++-------------------- Cargo.toml | 2 +- src/app_state.rs | 12 ++++- src/cmd.rs | 70 ++++++++++++++++++++++++++ src/exercise.rs | 145 ++++++++++++++++++++--------------------------------- src/main.rs | 29 +++++++++-- src/run.rs | 2 +- src/watch/state.rs | 5 +- 8 files changed, 176 insertions(+), 158 deletions(-) create mode 100644 src/cmd.rs (limited to 'Cargo.lock') diff --git a/Cargo.lock b/Cargo.lock index 5767267..f9b48bd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -271,16 +271,6 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" -[[package]] -name = "errno" -version = "0.3.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" -dependencies = [ - "libc", - "windows-sys 0.52.0", -] - [[package]] name = "filetime" version = "0.2.23" @@ -333,15 +323,6 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" -[[package]] -name = "home" -version = "0.5.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5" -dependencies = [ - "windows-sys 0.52.0", -] - [[package]] name = "indexmap" version = "2.2.6" @@ -419,12 +400,6 @@ version = "0.2.153" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" -[[package]] -name = "linux-raw-sys" -version = "0.4.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c" - [[package]] name = "lock_api" version = "0.4.11" @@ -664,19 +639,6 @@ version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56" -[[package]] -name = "rustix" -version = "0.38.34" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" -dependencies = [ - "bitflags 2.5.0", - "errno", - "libc", - "linux-raw-sys", - "windows-sys 0.52.0", -] - [[package]] name = "rustlings" version = "6.0.0-beta.3" @@ -692,8 +654,8 @@ dependencies = [ "ratatui", "rustlings-macros", "serde", + "serde_json", "toml_edit", - "which", ] [[package]] @@ -752,6 +714,17 @@ dependencies = [ "syn", ] +[[package]] +name = "serde_json" +version = "1.0.116" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e17db7126d17feb94eb3fad46bf1a96b034e8aacbc2e775fe81505f8b0b2813" +dependencies = [ + "itoa", + "ryu", + "serde", +] + [[package]] name = "serde_spanned" version = "0.6.5" @@ -935,18 +908,6 @@ version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" -[[package]] -name = "which" -version = "6.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8211e4f58a2b2805adfbefbc07bab82958fc91e3836339b1ab7ae32465dce0d7" -dependencies = [ - "either", - "home", - "rustix", - "winsafe", -] - [[package]] name = "winapi" version = "0.3.9" @@ -1126,12 +1087,6 @@ dependencies = [ "memchr", ] -[[package]] -name = "winsafe" -version = "0.0.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d135d17ab770252ad95e9a872d365cf3090e3be864a34ab46f48555993efc904" - [[package]] name = "zerocopy" version = "0.7.32" diff --git a/Cargo.toml b/Cargo.toml index 1bc26c9..a77c84f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -56,9 +56,9 @@ notify-debouncer-mini = "0.4.1" os_pipe = "1.1.5" ratatui = "0.26.2" rustlings-macros = { path = "rustlings-macros", version = "=6.0.0-beta.3" } +serde_json = "1.0.116" serde.workspace = true toml_edit.workspace = true -which = "6.0.1" [dev-dependencies] assert_cmd = "2.0.14" diff --git a/src/app_state.rs b/src/app_state.rs index 476b5a9..b980bdb 100644 --- a/src/app_state.rs +++ b/src/app_state.rs @@ -7,7 +7,7 @@ use crossterm::{ use std::{ fs::{self, File}, io::{Read, StdoutLock, Write}, - path::Path, + path::{Path, PathBuf}, process::{Command, Stdio}, }; @@ -39,6 +39,7 @@ pub struct AppState { final_message: String, file_buf: Vec, official_exercises: bool, + target_dir: PathBuf, } impl AppState { @@ -90,6 +91,7 @@ impl AppState { pub fn new( exercise_infos: Vec, final_message: String, + target_dir: PathBuf, ) -> (Self, StateFileStatus) { let exercises = exercise_infos .into_iter() @@ -127,6 +129,7 @@ impl AppState { final_message, file_buf: Vec::with_capacity(2048), official_exercises: !Path::new("info.toml").exists(), + target_dir, }; let state_file_status = slf.update_from_file(); @@ -154,6 +157,11 @@ impl AppState { &self.exercises[self.current_exercise_ind] } + #[inline] + pub fn target_dir(&self) -> &Path { + &self.target_dir + } + pub fn set_current_exercise_ind(&mut self, ind: usize) -> Result<()> { if ind >= self.exercises.len() { bail!(BAD_INDEX_ERR); @@ -313,7 +321,7 @@ impl AppState { write!(writer, "Running {exercise} ... ")?; writer.flush()?; - let success = exercise.run(&mut output)?; + let success = exercise.run(&mut output, &self.target_dir)?; if !success { writeln!(writer, "{}\n", "FAILED".red())?; diff --git a/src/cmd.rs b/src/cmd.rs new file mode 100644 index 0000000..28f21c5 --- /dev/null +++ b/src/cmd.rs @@ -0,0 +1,70 @@ +use anyhow::{Context, Result}; +use std::{io::Read, path::Path, process::Command}; + +pub fn run_cmd(mut cmd: Command, description: &str, output: &mut Vec) -> Result { + let (mut reader, writer) = os_pipe::pipe() + .with_context(|| format!("Failed to create a pipe to run the command `{description}``"))?; + + let writer_clone = writer.try_clone().with_context(|| { + format!("Failed to clone the pipe writer for the command `{description}`") + })?; + + let mut handle = cmd + .stdout(writer_clone) + .stderr(writer) + .spawn() + .with_context(|| format!("Failed to run the command `{description}`"))?; + + // Prevent pipe deadlock. + drop(cmd); + + reader + .read_to_end(output) + .with_context(|| format!("Failed to read the output of the command `{description}`"))?; + + output.push(b'\n'); + + handle + .wait() + .with_context(|| format!("Failed to wait on the command `{description}` to exit")) + .map(|status| status.success()) +} + +pub struct CargoCmd<'a> { + pub subcommand: &'a str, + pub args: &'a [&'a str], + pub exercise_name: &'a str, + pub description: &'a str, + pub hide_warnings: bool, + pub target_dir: &'a Path, + pub output: &'a mut Vec, + pub dev: bool, +} + +impl<'a> CargoCmd<'a> { + pub fn run(&mut self) -> Result { + let mut cmd = Command::new("cargo"); + cmd.arg(self.subcommand); + + // A hack to make `cargo run` work when developing Rustlings. + if self.dev { + cmd.arg("--manifest-path") + .arg("dev/Cargo.toml") + .arg("--target-dir") + .arg(self.target_dir); + } + + cmd.arg("--color") + .arg("always") + .arg("-q") + .arg("--bin") + .arg(self.exercise_name) + .args(self.args); + + if self.hide_warnings { + cmd.env("RUSTFLAGS", "-A warnings"); + } + + run_cmd(cmd, self.description, self.output) + } +} diff --git a/src/exercise.rs b/src/exercise.rs index 50f360e..23dae6f 100644 --- a/src/exercise.rs +++ b/src/exercise.rs @@ -1,57 +1,21 @@ -use anyhow::{Context, Result}; +use anyhow::Result; use crossterm::style::{style, StyledContent, Stylize}; use std::{ fmt::{self, Display, Formatter}, - io::{Read, Write}, - process::{Command, Stdio}, + io::Write, + path::{Path, PathBuf}, + process::Command, }; -use crate::{in_official_repo, terminal_link::TerminalFileLink, DEBUG_PROFILE}; +use crate::{ + cmd::{run_cmd, CargoCmd}, + in_official_repo, + terminal_link::TerminalFileLink, + DEBUG_PROFILE, +}; pub const OUTPUT_CAPACITY: usize = 1 << 14; -fn run_command( - mut cmd: Command, - cmd_description: &str, - output: &mut Vec, - stderr: bool, -) -> Result { - let (mut reader, writer) = os_pipe::pipe().with_context(|| { - format!("Failed to create a pipe to run the command `{cmd_description}``") - })?; - - let (stdout, stderr) = if stderr { - ( - Stdio::from(writer.try_clone().with_context(|| { - format!("Failed to clone the pipe writer for the command `{cmd_description}`") - })?), - Stdio::from(writer), - ) - } else { - (Stdio::from(writer), Stdio::null()) - }; - - let mut handle = cmd - .stdout(stdout) - .stderr(stderr) - .spawn() - .with_context(|| format!("Failed to run the command `{cmd_description}`"))?; - - // Prevent pipe deadlock. - drop(cmd); - - reader - .read_to_end(output) - .with_context(|| format!("Failed to read the output of the command `{cmd_description}`"))?; - - output.push(b'\n'); - - handle - .wait() - .with_context(|| format!("Failed to wait on the command `{cmd_description}` to exit")) - .map(|status| status.success()) -} - pub struct Exercise { pub dir: Option<&'static str>, // Exercise's unique name @@ -66,11 +30,16 @@ pub struct Exercise { } impl Exercise { - fn run_bin(&self, output: &mut Vec) -> Result { + fn run_bin(&self, output: &mut Vec, target_dir: &Path) -> Result { writeln!(output, "{}", "Output".underlined())?; - let bin_path = format!("target/debug/{}", self.name); - let success = run_command(Command::new(&bin_path), &bin_path, output, true)?; + let mut bin_path = + PathBuf::with_capacity(target_dir.as_os_str().len() + 7 + self.name.len()); + bin_path.push(target_dir); + bin_path.push("debug"); + bin_path.push(self.name); + + let success = run_cmd(Command::new(&bin_path), &bin_path.to_string_lossy(), output)?; if !success { writeln!( @@ -85,43 +54,23 @@ impl Exercise { Ok(success) } - fn cargo_cmd( - &self, - command: &str, - args: &[&str], - cmd_description: &str, - output: &mut Vec, - dev: bool, - stderr: bool, - ) -> Result { - let mut cmd = Command::new("cargo"); - cmd.arg(command); - - // A hack to make `cargo run` work when developing Rustlings. - if dev { - cmd.arg("--manifest-path") - .arg("dev/Cargo.toml") - .arg("--target-dir") - .arg("target"); - } - - cmd.arg("--color") - .arg("always") - .arg("-q") - .arg("--bin") - .arg(self.name) - .args(args); - - run_command(cmd, cmd_description, output, stderr) - } - - pub fn run(&self, output: &mut Vec) -> Result { + pub fn run(&self, output: &mut Vec, target_dir: &Path) -> Result { output.clear(); // Developing the official Rustlings. let dev = DEBUG_PROFILE && in_official_repo(); - let build_success = self.cargo_cmd("build", &[], "cargo build …", output, dev, true)?; + let build_success = CargoCmd { + subcommand: "build", + args: &[], + exercise_name: self.name, + description: "cargo build …", + hide_warnings: false, + target_dir, + output, + dev, + } + .run()?; if !build_success { return Ok(false); } @@ -134,19 +83,28 @@ impl Exercise { } else { &["--profile", "test"] }; - let clippy_success = - self.cargo_cmd("clippy", clippy_args, "cargo clippy …", output, dev, true)?; + let clippy_success = CargoCmd { + subcommand: "clippy", + args: clippy_args, + exercise_name: self.name, + description: "cargo clippy …", + hide_warnings: false, + target_dir, + output, + dev, + } + .run()?; if !clippy_success { return Ok(false); } if !self.test { - return self.run_bin(output); + return self.run_bin(output, target_dir); } - let test_success = self.cargo_cmd( - "test", - &[ + let test_success = CargoCmd { + subcommand: "test", + args: &[ "--", "--color", "always", @@ -154,14 +112,17 @@ impl Exercise { "--format", "pretty", ], - "cargo test …", + exercise_name: self.name, + description: "cargo test …", + // Hide warnings because they are shown by Clippy. + hide_warnings: true, + target_dir, output, dev, - // Hide warnings because they are shown by Clippy. - false, - )?; + } + .run()?; - let run_success = self.run_bin(output)?; + let run_success = self.run_bin(output, target_dir)?; Ok(test_success && run_success) } diff --git a/src/main.rs b/src/main.rs index 7a142fd..b03aa52 100644 --- a/src/main.rs +++ b/src/main.rs @@ -5,16 +5,18 @@ use crossterm::{ terminal::{Clear, ClearType}, ExecutableCommand, }; +use serde::Deserialize; use std::{ io::{self, BufRead, Write}, - path::Path, - process::exit, + path::{Path, PathBuf}, + process::{exit, Command, Stdio}, }; use self::{app_state::AppState, dev::DevCommands, info_file::InfoFile, watch::WatchExit}; mod app_state; mod cargo_toml; +mod cmd; mod dev; mod embedded; mod exercise; @@ -75,6 +77,11 @@ enum Subcommands { Dev(DevCommands), } +#[derive(Deserialize)] +struct CargoMetadata { + target_directory: PathBuf, +} + fn in_official_repo() -> bool { Path::new("dev/rustlings-repo.txt").exists() } @@ -86,7 +93,20 @@ fn main() -> Result<()> { bail!("{OLD_METHOD_ERR}"); } - which::which("cargo").context(CARGO_NOT_FOUND_ERR)?; + let metadata_output = Command::new("cargo") + .arg("metadata") + .arg("-q") + .arg("--format-version") + .arg("1") + .arg("--no-deps") + .stdin(Stdio::null()) + .stderr(Stdio::inherit()) + .output() + .context(CARGO_METADATA_ERR)? + .stdout; + let target_dir = serde_json::de::from_slice::(&metadata_output) + .context("Failed to read the field `target_directory` from the `cargo metadata` output")? + .target_directory; match args.command { Some(Subcommands::Init) => { @@ -122,6 +142,7 @@ fn main() -> Result<()> { let (mut app_state, state_file_status) = AppState::new( info_file.exercises, info_file.final_message.unwrap_or_default(), + target_dir, ); if let Some(welcome_message) = info_file.welcome_message { @@ -198,7 +219,7 @@ The new method doesn't include cloning the Rustlings' repository. Please follow the instructions in the README: https://github.com/rust-lang/rustlings#getting-started"; -const CARGO_NOT_FOUND_ERR: &str = "Failed to find `cargo`. +const CARGO_METADATA_ERR: &str = "Failed to run the command `cargo metadata …` Did you already install Rust? Try running `cargo --version` to diagnose the problem."; diff --git a/src/run.rs b/src/run.rs index cbc9ad7..9b5ddd3 100644 --- a/src/run.rs +++ b/src/run.rs @@ -11,7 +11,7 @@ use crate::{ pub fn run(app_state: &mut AppState) -> Result<()> { let exercise = app_state.current_exercise(); let mut output = Vec::with_capacity(OUTPUT_CAPACITY); - let success = exercise.run(&mut output)?; + let success = exercise.run(&mut output, app_state.target_dir())?; let mut stdout = io::stdout().lock(); stdout.write_all(&output)?; diff --git a/src/watch/state.rs b/src/watch/state.rs index 40c01bf..82b745a 100644 --- a/src/watch/state.rs +++ b/src/watch/state.rs @@ -50,7 +50,10 @@ impl<'a> WatchState<'a> { pub fn run_current_exercise(&mut self) -> Result<()> { self.show_hint = false; - let success = self.app_state.current_exercise().run(&mut self.output)?; + let success = self + .app_state + .current_exercise() + .run(&mut self.output, self.app_state.target_dir())?; if success { self.done_status = if let Some(solution_path) = self.app_state.current_solution_path()? { -- cgit v1.2.3 From 181c81f0165b71b5eda5e35c04849d9ac30d0261 Mon Sep 17 00:00:00 2001 From: mo8it Date: Sat, 27 Apr 2024 04:17:24 +0200 Subject: chore: Release --- Cargo.lock | 4 ++-- Cargo.toml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'Cargo.lock') diff --git a/Cargo.lock b/Cargo.lock index f9b48bd..e142dfc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -641,7 +641,7 @@ checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56" [[package]] name = "rustlings" -version = "6.0.0-beta.3" +version = "6.0.0-beta.4" dependencies = [ "anyhow", "assert_cmd", @@ -660,7 +660,7 @@ dependencies = [ [[package]] name = "rustlings-macros" -version = "6.0.0-beta.3" +version = "6.0.0-beta.4" dependencies = [ "quote", "serde", diff --git a/Cargo.toml b/Cargo.toml index a77c84f..7e52608 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,7 +8,7 @@ exclude = [ ] [workspace.package] -version = "6.0.0-beta.3" +version = "6.0.0-beta.4" authors = [ "Liv ", "Mo Bitar ", @@ -55,7 +55,7 @@ hashbrown = "0.14.3" notify-debouncer-mini = "0.4.1" os_pipe = "1.1.5" ratatui = "0.26.2" -rustlings-macros = { path = "rustlings-macros", version = "=6.0.0-beta.3" } +rustlings-macros = { path = "rustlings-macros", version = "=6.0.0-beta.4" } serde_json = "1.0.116" serde.workspace = true toml_edit.workspace = true -- cgit v1.2.3 From c3a92b1248201e4d09b21ba2ff0791b1375f83f0 Mon Sep 17 00:00:00 2001 From: mo8it Date: Sat, 27 Apr 2024 04:21:29 +0200 Subject: Update deps --- Cargo.lock | 35 ++++++++++++++++++++++------------- 1 file changed, 22 insertions(+), 13 deletions(-) (limited to 'Cargo.lock') diff --git a/Cargo.lock b/Cargo.lock index e142dfc..0a35aa2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -279,7 +279,7 @@ checksum = "1ee447700ac8aa0b2f2bd7bc4462ad686ba06baa6727ac149a2d6277f0d240fd" dependencies = [ "cfg-if", "libc", - "redox_syscall", + "redox_syscall 0.4.1", "windows-sys 0.52.0", ] @@ -402,9 +402,9 @@ checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" [[package]] name = "lock_api" -version = "0.4.11" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45" +checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" dependencies = [ "autocfg", "scopeguard", @@ -506,9 +506,9 @@ dependencies = [ [[package]] name = "parking_lot" -version = "0.12.1" +version = "0.12.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +checksum = "7e4af0ca4f6caed20e900d564c242b8e5d4903fdacf31d3daf527b66fe6f42fb" dependencies = [ "lock_api", "parking_lot_core", @@ -516,15 +516,15 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.9.9" +version = "0.9.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e" +checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" dependencies = [ "cfg-if", "libc", - "redox_syscall", + "redox_syscall 0.5.1", "smallvec", - "windows-targets 0.48.5", + "windows-targets 0.52.5", ] [[package]] @@ -610,6 +610,15 @@ dependencies = [ "bitflags 1.3.2", ] +[[package]] +name = "redox_syscall" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "469052894dcb553421e483e4209ee581a45100d31b4018de03e5a7ad86374a7e" +dependencies = [ + "bitflags 2.5.0", +] + [[package]] name = "regex" version = "1.10.4" @@ -867,9 +876,9 @@ checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202" [[package]] name = "unicode-width" -version = "0.1.11" +version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85" +checksum = "68f5e5f3158ecfd4b8ff6fe086db7c8467a2dfdac97fe420f2b7c4aa97af66d6" [[package]] name = "utf8parse" @@ -1080,9 +1089,9 @@ checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" [[package]] name = "winnow" -version = "0.6.6" +version = "0.6.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0c976aaaa0e1f90dbb21e9587cdaf1d9679a1cde8875c0d6bd83ab96a208352" +checksum = "14b9415ee827af173ebb3f15f9083df5a122eb93572ec28741fb153356ea2578" dependencies = [ "memchr", ] -- cgit v1.2.3 From 12504b01e910cd9066a9d8a6c41896470033b0c2 Mon Sep 17 00:00:00 2001 From: mo8it Date: Sat, 27 Apr 2024 04:32:06 +0200 Subject: Disable unneeded features in deps --- Cargo.lock | 1 - Cargo.toml | 5 ++--- 2 files changed, 2 insertions(+), 4 deletions(-) (limited to 'Cargo.lock') diff --git a/Cargo.lock b/Cargo.lock index 0a35aa2..6a47c31 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -474,7 +474,6 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5d40b221972a1fc5ef4d858a2f671fb34c75983eb385463dff3780eeff6a9d43" dependencies = [ - "crossbeam-channel", "log", "notify", ] diff --git a/Cargo.toml b/Cargo.toml index 7e52608..38ebb39 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -26,7 +26,6 @@ toml_edit = { version = "0.22.12", default-features = false, features = ["parse" [package] name = "rustlings" description = "Small exercises to get you used to reading and writing Rust code!" -default-run = "rustlings" version.workspace = true authors.workspace = true repository.workspace = true @@ -52,9 +51,9 @@ anyhow = "1.0.82" clap = { version = "4.5.4", features = ["derive"] } crossterm = "0.27.0" hashbrown = "0.14.3" -notify-debouncer-mini = "0.4.1" +notify-debouncer-mini = { version = "0.4.1", default-features = false } os_pipe = "1.1.5" -ratatui = "0.26.2" +ratatui = { version = "0.26.2", default-features = false, features = ["crossterm"] } rustlings-macros = { path = "rustlings-macros", version = "=6.0.0-beta.4" } serde_json = "1.0.116" serde.workspace = true -- cgit v1.2.3 From 016e6a014ee77d13ce5d7fc4e1a140276143c4b2 Mon Sep 17 00:00:00 2001 From: mo8it Date: Sat, 27 Apr 2024 17:32:42 +0200 Subject: Update serde --- Cargo.lock | 8 ++++---- Cargo.toml | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) (limited to 'Cargo.lock') diff --git a/Cargo.lock b/Cargo.lock index 6a47c31..6cf6a30 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -704,18 +704,18 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "serde" -version = "1.0.198" +version = "1.0.199" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9846a40c979031340571da2545a4e5b7c4163bdae79b301d5f86d03979451fcc" +checksum = "0c9f6e76df036c77cd94996771fb40db98187f096dd0b9af39c6c6e452ba966a" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.198" +version = "1.0.199" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e88edab869b01783ba905e7d0153f9fc1a6505a96e4ad3018011eedb838566d9" +checksum = "11bd257a6541e141e42ca6d24ae26f7714887b47e89aa739099104c7e4d3b7fc" dependencies = [ "proc-macro2", "quote", diff --git a/Cargo.toml b/Cargo.toml index 38ebb39..e430cf1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,7 +20,7 @@ license = "MIT" edition = "2021" [workspace.dependencies] -serde = { version = "1.0.198", features = ["derive"] } +serde = { version = "1.0.199", features = ["derive"] } toml_edit = { version = "0.22.12", default-features = false, features = ["parse", "serde"] } [package] -- cgit v1.2.3 From 89e0f642791cf9e66143c0904501333cefbb8a61 Mon Sep 17 00:00:00 2001 From: mo8it Date: Sat, 27 Apr 2024 17:35:08 +0200 Subject: chore: Release --- Cargo.lock | 4 ++-- Cargo.toml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'Cargo.lock') diff --git a/Cargo.lock b/Cargo.lock index 6cf6a30..1c5463f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -649,7 +649,7 @@ checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56" [[package]] name = "rustlings" -version = "6.0.0-beta.4" +version = "6.0.0-beta.5" dependencies = [ "anyhow", "assert_cmd", @@ -668,7 +668,7 @@ dependencies = [ [[package]] name = "rustlings-macros" -version = "6.0.0-beta.4" +version = "6.0.0-beta.5" dependencies = [ "quote", "serde", diff --git a/Cargo.toml b/Cargo.toml index e430cf1..41ad49c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,7 +8,7 @@ exclude = [ ] [workspace.package] -version = "6.0.0-beta.4" +version = "6.0.0-beta.5" authors = [ "Liv ", "Mo Bitar ", @@ -54,7 +54,7 @@ hashbrown = "0.14.3" notify-debouncer-mini = { version = "0.4.1", default-features = false } os_pipe = "1.1.5" ratatui = { version = "0.26.2", default-features = false, features = ["crossterm"] } -rustlings-macros = { path = "rustlings-macros", version = "=6.0.0-beta.4" } +rustlings-macros = { path = "rustlings-macros", version = "=6.0.0-beta.5" } serde_json = "1.0.116" serde.workspace = true toml_edit.workspace = true -- cgit v1.2.3 From 196d3c1a9820acc9c58134f62a08ec3671411777 Mon Sep 17 00:00:00 2001 From: mo8it Date: Mon, 29 Apr 2024 00:36:13 +0200 Subject: Bump version --- Cargo.lock | 4 ++-- Cargo.toml | 2 +- README.md | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) (limited to 'Cargo.lock') diff --git a/Cargo.lock b/Cargo.lock index 1c5463f..597ee06 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -303,9 +303,9 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.14.3" +version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" dependencies = [ "ahash", "allocator-api2", diff --git a/Cargo.toml b/Cargo.toml index 41ad49c..2054b77 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -50,7 +50,7 @@ include = [ anyhow = "1.0.82" clap = { version = "4.5.4", features = ["derive"] } crossterm = "0.27.0" -hashbrown = "0.14.3" +hashbrown = "0.14.5" notify-debouncer-mini = { version = "0.4.1", default-features = false } os_pipe = "1.1.5" ratatui = { version = "0.26.2", default-features = false, features = ["crossterm"] } diff --git a/README.md b/README.md index 52848fe..3ba080f 100644 --- a/README.md +++ b/README.md @@ -35,7 +35,7 @@ The following command will download and compile Rustlings: ```bash -cargo install rustlings@6.0.0-beta.5 +cargo install rustlings@6.0.0-beta.6 ```

@@ -44,7 +44,7 @@ cargo install rustlings@6.0.0-beta.5 - Make sure you have the latest Rust version by running `rustup update` -- Try adding the `--locked` flag: `cargo install rustlings@6.0.0-beta.5 --locked` +- Try adding the `--locked` flag: `cargo install rustlings@6.0.0-beta.6 --locked` - Otherwise, please [report the issue](https://github.com/rust-lang/rustlings/issues/new)
-- cgit v1.2.3 From 7f73219041fc6659d2e8e944c4e1d0341e323478 Mon Sep 17 00:00:00 2001 From: mo8it Date: Mon, 29 Apr 2024 00:36:50 +0200 Subject: chore: Release --- Cargo.lock | 4 ++-- Cargo.toml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'Cargo.lock') diff --git a/Cargo.lock b/Cargo.lock index 597ee06..271d09a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -649,7 +649,7 @@ checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56" [[package]] name = "rustlings" -version = "6.0.0-beta.5" +version = "6.0.0-beta.6" dependencies = [ "anyhow", "assert_cmd", @@ -668,7 +668,7 @@ dependencies = [ [[package]] name = "rustlings-macros" -version = "6.0.0-beta.5" +version = "6.0.0-beta.6" dependencies = [ "quote", "serde", diff --git a/Cargo.toml b/Cargo.toml index 2054b77..efb1512 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,7 +8,7 @@ exclude = [ ] [workspace.package] -version = "6.0.0-beta.5" +version = "6.0.0-beta.6" authors = [ "Liv ", "Mo Bitar ", @@ -54,7 +54,7 @@ hashbrown = "0.14.5" notify-debouncer-mini = { version = "0.4.1", default-features = false } os_pipe = "1.1.5" ratatui = { version = "0.26.2", default-features = false, features = ["crossterm"] } -rustlings-macros = { path = "rustlings-macros", version = "=6.0.0-beta.5" } +rustlings-macros = { path = "rustlings-macros", version = "=6.0.0-beta.6" } serde_json = "1.0.116" serde.workspace = true toml_edit.workspace = true -- cgit v1.2.3 From da9f97b0e0a54747202c43c6aed8346cc784cb02 Mon Sep 17 00:00:00 2001 From: mo8it Date: Sun, 12 May 2024 01:35:30 +0200 Subject: Update deps --- Cargo.lock | 91 +++++++++++++++++++++++++++++++++----------------------------- Cargo.toml | 4 +-- 2 files changed, 51 insertions(+), 44 deletions(-) (limited to 'Cargo.lock') diff --git a/Cargo.lock b/Cargo.lock index 271d09a..7a6049e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -31,47 +31,48 @@ checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f" [[package]] name = "anstream" -version = "0.6.13" +version = "0.6.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d96bd03f33fe50a863e394ee9718a706f988b9079b20c3784fb726e7678b62fb" +checksum = "418c75fa768af9c03be99d17643f93f79bbba589895012a80e3452a19ddda15b" dependencies = [ "anstyle", "anstyle-parse", "anstyle-query", "anstyle-wincon", "colorchoice", + "is_terminal_polyfill", "utf8parse", ] [[package]] name = "anstyle" -version = "1.0.6" +version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8901269c6307e8d93993578286ac0edf7f195079ffff5ebdeea6a59ffb7e36bc" +checksum = "038dfcf04a5feb68e9c60b21c9625a54c2c0616e79b72b0fd87075a056ae1d1b" [[package]] name = "anstyle-parse" -version = "0.2.3" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c75ac65da39e5fe5ab759307499ddad880d724eed2f6ce5b5e8a26f4f387928c" +checksum = "c03a11a9034d92058ceb6ee011ce58af4a9bf61491aa7e1e59ecd24bd40d22d4" dependencies = [ "utf8parse", ] [[package]] name = "anstyle-query" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e28923312444cdd728e4738b3f9c9cac739500909bb3d3c94b43551b16517648" +checksum = "a64c907d4e79225ac72e2a354c9ce84d50ebb4586dee56c82b3ee73004f537f5" dependencies = [ "windows-sys 0.52.0", ] [[package]] name = "anstyle-wincon" -version = "3.0.2" +version = "3.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1cd54b81ec8d6180e24654d0b371ad22fc3dd083b6ff8ba325b72e00c87660a7" +checksum = "61a38449feb7068f52bb06c12759005cf459ee52bb4adc1d5a7c4322d716fb19" dependencies = [ "anstyle", "windows-sys 0.52.0", @@ -79,9 +80,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.82" +version = "1.0.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f538837af36e6f6a9be0faa67f9a314f8119e4e4b5867c6ab40ed60360142519" +checksum = "25bdb32cbbdce2b519a9cd7df3a678443100e265d5e25ca763b7572a5104f5f3" [[package]] name = "assert_cmd" @@ -100,9 +101,9 @@ dependencies = [ [[package]] name = "autocfg" -version = "1.2.0" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1fdabc7756949593fe60f30ec81974b613357de856987752631dea1e3394c80" +checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" [[package]] name = "bitflags" @@ -190,9 +191,9 @@ checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce" [[package]] name = "colorchoice" -version = "1.0.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" +checksum = "0b6a852b24ab71dffc585bcb46eaf7959d175cb865a7152e35b348d1b2960422" [[package]] name = "compact_str" @@ -359,6 +360,12 @@ dependencies = [ "libc", ] +[[package]] +name = "is_terminal_polyfill" +version = "1.70.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8478577c03552c21db0e2724ffb8986a5ce7af88107e6be5d2ee6e158c12800" + [[package]] name = "itertools" version = "0.12.1" @@ -396,9 +403,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.153" +version = "0.2.154" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" +checksum = "ae743338b92ff9146ce83992f766a31066a91a8c84a45e0e9f21e7cf6de6d346" [[package]] name = "lock_api" @@ -480,9 +487,9 @@ dependencies = [ [[package]] name = "num-traits" -version = "0.2.18" +version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" dependencies = [ "autocfg", ] @@ -528,9 +535,9 @@ dependencies = [ [[package]] name = "paste" -version = "1.0.14" +version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" [[package]] name = "predicates" @@ -564,9 +571,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.81" +version = "1.0.82" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d1597b0c024618f09a9c3b8655b7e430397a36d23fdafec26d6965e9eec3eba" +checksum = "8ad3d49ab951a01fbaafe34f2ec74122942fe18a3f9814c3268f1bb72042131b" dependencies = [ "unicode-ident", ] @@ -677,15 +684,15 @@ dependencies = [ [[package]] name = "rustversion" -version = "1.0.15" +version = "1.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80af6f9131f277a45a3fba6ce8e2258037bb0477a67e610d3c1fe046ab31de47" +checksum = "092474d1a01ea8278f69e6a358998405fae5b8b963ddaeb2b0b04a128bf1dfb0" [[package]] name = "ryu" -version = "1.0.17" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1" +checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" [[package]] name = "same-file" @@ -704,18 +711,18 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "serde" -version = "1.0.199" +version = "1.0.201" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c9f6e76df036c77cd94996771fb40db98187f096dd0b9af39c6c6e452ba966a" +checksum = "780f1cebed1629e4753a1a38a3c72d30b97ec044f0aef68cb26650a3c5cf363c" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.199" +version = "1.0.201" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11bd257a6541e141e42ca6d24ae26f7714887b47e89aa739099104c7e4d3b7fc" +checksum = "c5e405930b9796f1c00bee880d03fc7e0bb4b9a11afc776885ffe84320da2865" dependencies = [ "proc-macro2", "quote", @@ -724,9 +731,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.116" +version = "1.0.117" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e17db7126d17feb94eb3fad46bf1a96b034e8aacbc2e775fe81505f8b0b2813" +checksum = "455182ea6142b14f93f4bc5320a2b31c1f266b66a4a5c858b013302a5d8cbfc3" dependencies = [ "itoa", "ryu", @@ -824,9 +831,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.60" +version = "2.0.63" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "909518bc7b1c9b779f1bbf07f2929d35af9f0f37e47c6e9ef7f9dddc1e1821f3" +checksum = "bf5be731623ca1a1fb7d8be6f261a3be6d3e2337b8a1f97be944d020c8fcb704" dependencies = [ "proc-macro2", "quote", @@ -1088,27 +1095,27 @@ checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" [[package]] name = "winnow" -version = "0.6.7" +version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14b9415ee827af173ebb3f15f9083df5a122eb93572ec28741fb153356ea2578" +checksum = "c3c52e9c97a68071b23e836c9380edae937f17b9c4667bd021973efc689f618d" dependencies = [ "memchr", ] [[package]] name = "zerocopy" -version = "0.7.32" +version = "0.7.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74d4d3961e53fa4c9a25a8637fc2bfaf2595b3d3ae34875568a5cf64787716be" +checksum = "ae87e3fcd617500e5d106f0380cf7b77f3c6092aae37191433159dda23cfb087" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.7.32" +version = "0.7.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" +checksum = "15e934569e47891f7d9411f1a451d947a60e000ab3bd24fbb970f000387d1b3b" dependencies = [ "proc-macro2", "quote", diff --git a/Cargo.toml b/Cargo.toml index efb1512..bc10d02 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -47,7 +47,7 @@ include = [ ] [dependencies] -anyhow = "1.0.82" +anyhow = "1.0.83" clap = { version = "4.5.4", features = ["derive"] } crossterm = "0.27.0" hashbrown = "0.14.5" @@ -55,7 +55,7 @@ notify-debouncer-mini = { version = "0.4.1", default-features = false } os_pipe = "1.1.5" ratatui = { version = "0.26.2", default-features = false, features = ["crossterm"] } rustlings-macros = { path = "rustlings-macros", version = "=6.0.0-beta.6" } -serde_json = "1.0.116" +serde_json = "1.0.117" serde.workspace = true toml_edit.workspace = true -- cgit v1.2.3 From a4da216a5c52cf5626e1861991d1172c5897e746 Mon Sep 17 00:00:00 2001 From: mo8it Date: Mon, 13 May 2024 02:46:26 +0200 Subject: chore: Release --- Cargo.lock | 4 ++-- Cargo.toml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'Cargo.lock') diff --git a/Cargo.lock b/Cargo.lock index 7a6049e..aebbfc5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -656,7 +656,7 @@ checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56" [[package]] name = "rustlings" -version = "6.0.0-beta.6" +version = "6.0.0-beta.7" dependencies = [ "anyhow", "assert_cmd", @@ -675,7 +675,7 @@ dependencies = [ [[package]] name = "rustlings-macros" -version = "6.0.0-beta.6" +version = "6.0.0-beta.7" dependencies = [ "quote", "serde", diff --git a/Cargo.toml b/Cargo.toml index f2015cb..eacae3b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,7 +8,7 @@ exclude = [ ] [workspace.package] -version = "6.0.0-beta.6" +version = "6.0.0-beta.7" authors = [ "Liv ", "Mo Bitar ", @@ -53,7 +53,7 @@ hashbrown = "0.14.5" notify-debouncer-mini = { version = "0.4.1", default-features = false } os_pipe = "1.1.5" ratatui = { version = "0.26.2", default-features = false, features = ["crossterm"] } -rustlings-macros = { path = "rustlings-macros", version = "=6.0.0-beta.6" } +rustlings-macros = { path = "rustlings-macros", version = "=6.0.0-beta.7" } serde_json = "1.0.117" serde.workspace = true toml_edit.workspace = true -- cgit v1.2.3 From 56eb4a5d650e6bb671fe60b8cc746b19e9c5f3d4 Mon Sep 17 00:00:00 2001 From: mo8it Date: Mon, 13 May 2024 04:11:29 +0200 Subject: chore: Release --- Cargo.lock | 4 ++-- Cargo.toml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'Cargo.lock') diff --git a/Cargo.lock b/Cargo.lock index aebbfc5..7c1d1a6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -656,7 +656,7 @@ checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56" [[package]] name = "rustlings" -version = "6.0.0-beta.7" +version = "6.0.0-beta.8" dependencies = [ "anyhow", "assert_cmd", @@ -675,7 +675,7 @@ dependencies = [ [[package]] name = "rustlings-macros" -version = "6.0.0-beta.7" +version = "6.0.0-beta.8" dependencies = [ "quote", "serde", diff --git a/Cargo.toml b/Cargo.toml index eacae3b..e552590 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,7 +8,7 @@ exclude = [ ] [workspace.package] -version = "6.0.0-beta.7" +version = "6.0.0-beta.8" authors = [ "Liv ", "Mo Bitar ", @@ -53,7 +53,7 @@ hashbrown = "0.14.5" notify-debouncer-mini = { version = "0.4.1", default-features = false } os_pipe = "1.1.5" ratatui = { version = "0.26.2", default-features = false, features = ["crossterm"] } -rustlings-macros = { path = "rustlings-macros", version = "=6.0.0-beta.7" } +rustlings-macros = { path = "rustlings-macros", version = "=6.0.0-beta.8" } serde_json = "1.0.117" serde.workspace = true toml_edit.workspace = true -- cgit v1.2.3 From 0add5ac240909e2744b492b41a310c9598c1e622 Mon Sep 17 00:00:00 2001 From: mo8it Date: Mon, 13 May 2024 17:14:11 +0200 Subject: chore: Release --- Cargo.lock | 4 ++-- Cargo.toml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'Cargo.lock') diff --git a/Cargo.lock b/Cargo.lock index 7c1d1a6..20a628c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -656,7 +656,7 @@ checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56" [[package]] name = "rustlings" -version = "6.0.0-beta.8" +version = "6.0.0-beta.9" dependencies = [ "anyhow", "assert_cmd", @@ -675,7 +675,7 @@ dependencies = [ [[package]] name = "rustlings-macros" -version = "6.0.0-beta.8" +version = "6.0.0-beta.9" dependencies = [ "quote", "serde", diff --git a/Cargo.toml b/Cargo.toml index e552590..508fdcc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,7 +8,7 @@ exclude = [ ] [workspace.package] -version = "6.0.0-beta.8" +version = "6.0.0-beta.9" authors = [ "Liv ", "Mo Bitar ", @@ -53,7 +53,7 @@ hashbrown = "0.14.5" notify-debouncer-mini = { version = "0.4.1", default-features = false } os_pipe = "1.1.5" ratatui = { version = "0.26.2", default-features = false, features = ["crossterm"] } -rustlings-macros = { path = "rustlings-macros", version = "=6.0.0-beta.8" } +rustlings-macros = { path = "rustlings-macros", version = "=6.0.0-beta.9" } serde_json = "1.0.117" serde.workspace = true toml_edit.workspace = true -- cgit v1.2.3 From bde2524c3b1043da489541d4885592abe16646fa Mon Sep 17 00:00:00 2001 From: mo8it Date: Mon, 20 May 2024 18:11:19 +0200 Subject: Update deps --- Cargo.lock | 74 +++++++++++++++++++++++++++++++++----------------------------- Cargo.toml | 8 +++---- 2 files changed, 43 insertions(+), 39 deletions(-) (limited to 'Cargo.lock') diff --git a/Cargo.lock b/Cargo.lock index 20a628c..10dfa30 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -80,9 +80,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.83" +version = "1.0.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25bdb32cbbdce2b519a9cd7df3a678443100e265d5e25ca763b7572a5104f5f3" +checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da" [[package]] name = "assert_cmd" @@ -210,18 +210,18 @@ dependencies = [ [[package]] name = "crossbeam-channel" -version = "0.5.12" +version = "0.5.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab3db02a9c5b5121e1e42fbdb1aeb65f5e02624cc58c43f2884c6ccac0b82f95" +checksum = "33480d6946193aa8033910124896ca395333cae7e2d1113d1fef6c3272217df2" dependencies = [ "crossbeam-utils", ] [[package]] name = "crossbeam-utils" -version = "0.8.19" +version = "0.8.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345" +checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" [[package]] name = "crossterm" @@ -262,9 +262,9 @@ checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10" [[package]] name = "either" -version = "1.11.0" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a47c1c47d2f5964e29c61246e81db715514cd532db6b5116a25ea3c03d6780a2" +checksum = "3dca9240753cf90908d7e4aac30f630662b02aebaa1b58a3cadabdb23385b58b" [[package]] name = "equivalent" @@ -334,12 +334,6 @@ dependencies = [ "hashbrown", ] -[[package]] -name = "indoc" -version = "2.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b248f5224d1d606005e02c97f5aa4e88eeb230488bcc03bc9ca4d7991399f2b5" - [[package]] name = "inotify" version = "0.9.6" @@ -403,9 +397,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.154" +version = "0.2.155" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae743338b92ff9146ce83992f766a31066a91a8c84a45e0e9f21e7cf6de6d346" +checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" [[package]] name = "lock_api" @@ -571,9 +565,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.82" +version = "1.0.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ad3d49ab951a01fbaafe34f2ec74122942fe18a3f9814c3268f1bb72042131b" +checksum = "0b33eb56c327dec362a9e55b3ad14f9d2f0904fb5a5b03b513ab5465399e9f43" dependencies = [ "unicode-ident", ] @@ -589,21 +583,21 @@ dependencies = [ [[package]] name = "ratatui" -version = "0.26.2" +version = "0.26.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a564a852040e82671dc50a37d88f3aa83bbc690dfc6844cfe7a2591620206a80" +checksum = "f44c9e68fd46eda15c646fbb85e1040b657a58cdc8c98db1d97a55930d991eef" dependencies = [ "bitflags 2.5.0", "cassowary", "compact_str", "crossterm", - "indoc", "itertools", "lru", "paste", "stability", "strum", "unicode-segmentation", + "unicode-truncate", "unicode-width", ] @@ -684,9 +678,9 @@ dependencies = [ [[package]] name = "rustversion" -version = "1.0.16" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "092474d1a01ea8278f69e6a358998405fae5b8b963ddaeb2b0b04a128bf1dfb0" +checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6" [[package]] name = "ryu" @@ -711,18 +705,18 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "serde" -version = "1.0.201" +version = "1.0.202" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "780f1cebed1629e4753a1a38a3c72d30b97ec044f0aef68cb26650a3c5cf363c" +checksum = "226b61a0d411b2ba5ff6d7f73a476ac4f8bb900373459cd00fab8512828ba395" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.201" +version = "1.0.202" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5e405930b9796f1c00bee880d03fc7e0bb4b9a11afc776885ffe84320da2865" +checksum = "6048858004bcff69094cd972ed40a32500f153bd3be9f716b2eed2e8217c4838" dependencies = [ "proc-macro2", "quote", @@ -742,9 +736,9 @@ dependencies = [ [[package]] name = "serde_spanned" -version = "0.6.5" +version = "0.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb3622f419d1296904700073ea6cc23ad690adbd66f13ea683df73298736f0c1" +checksum = "79e674e01f999af37c49f70a6ede167a8a60b2503e56c5599532a65baa5969a0" dependencies = [ "serde", ] @@ -831,9 +825,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.63" +version = "2.0.65" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf5be731623ca1a1fb7d8be6f261a3be6d3e2337b8a1f97be944d020c8fcb704" +checksum = "d2863d96a84c6439701d7a38f9de935ec562c8832cc55d1dde0f513b52fad106" dependencies = [ "proc-macro2", "quote", @@ -848,18 +842,18 @@ checksum = "3369f5ac52d5eb6ab48c6b4ffdc8efbcad6b89c765749064ba298f2c68a16a76" [[package]] name = "toml_datetime" -version = "0.6.5" +version = "0.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1" +checksum = "4badfd56924ae69bcc9039335b2e017639ce3f9b001c393c1b2d1ef846ce2cbf" dependencies = [ "serde", ] [[package]] name = "toml_edit" -version = "0.22.12" +version = "0.22.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3328d4f68a705b2a4498da1d580585d39a6510f98318a2cec3018a7ec61ddef" +checksum = "c127785850e8c20836d49732ae6abfa47616e60bf9d9f57c43c250361a9db96c" dependencies = [ "indexmap", "serde", @@ -880,6 +874,16 @@ version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202" +[[package]] +name = "unicode-truncate" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a5fbabedabe362c618c714dbefda9927b5afc8e2a8102f47f081089a9019226" +dependencies = [ + "itertools", + "unicode-width", +] + [[package]] name = "unicode-width" version = "0.1.12" diff --git a/Cargo.toml b/Cargo.toml index 508fdcc..ebcbe6c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,8 +20,8 @@ license = "MIT" edition = "2021" [workspace.dependencies] -serde = { version = "1.0.199", features = ["derive"] } -toml_edit = { version = "0.22.12", default-features = false, features = ["parse", "serde"] } +serde = { version = "1.0.202", features = ["derive"] } +toml_edit = { version = "0.22.13", default-features = false, features = ["parse", "serde"] } [package] name = "rustlings" @@ -46,13 +46,13 @@ include = [ ] [dependencies] -anyhow = "1.0.83" +anyhow = "1.0.86" clap = { version = "4.5.4", features = ["derive"] } crossterm = "0.27.0" hashbrown = "0.14.5" notify-debouncer-mini = { version = "0.4.1", default-features = false } os_pipe = "1.1.5" -ratatui = { version = "0.26.2", default-features = false, features = ["crossterm"] } +ratatui = { version = "0.26.3", default-features = false, features = ["crossterm"] } rustlings-macros = { path = "rustlings-macros", version = "=6.0.0-beta.9" } serde_json = "1.0.117" serde.workspace = true -- cgit v1.2.3 From 8d4145038d8b1a2a31314669cc17dc42b1c8e616 Mon Sep 17 00:00:00 2001 From: mo8it Date: Sat, 25 May 2024 16:15:35 +0200 Subject: Update deps --- Cargo.lock | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'Cargo.lock') diff --git a/Cargo.lock b/Cargo.lock index 10dfa30..e1044ff 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -506,9 +506,9 @@ dependencies = [ [[package]] name = "parking_lot" -version = "0.12.2" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e4af0ca4f6caed20e900d564c242b8e5d4903fdacf31d3daf527b66fe6f42fb" +checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" dependencies = [ "lock_api", "parking_lot_core", @@ -825,9 +825,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.65" +version = "2.0.66" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2863d96a84c6439701d7a38f9de935ec562c8832cc55d1dde0f513b52fad106" +checksum = "c42f3f41a2de00b01c0aaad383c5a45241efc8b2d1eda5661812fda5f3cdcff5" dependencies = [ "proc-macro2", "quote", -- cgit v1.2.3 From c324ea10df1e8a19cd9fae9be0eda28d99b9f2e5 Mon Sep 17 00:00:00 2001 From: mo8it Date: Sat, 1 Jun 2024 15:10:17 +0200 Subject: Update deps --- Cargo.lock | 16 ++++++++-------- Cargo.toml | 2 +- 2 files changed, 9 insertions(+), 9 deletions(-) (limited to 'Cargo.lock') diff --git a/Cargo.lock b/Cargo.lock index e1044ff..fec0365 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -565,9 +565,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.83" +version = "1.0.84" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b33eb56c327dec362a9e55b3ad14f9d2f0904fb5a5b03b513ab5465399e9f43" +checksum = "ec96c6a92621310b51366f1e28d05ef11489516e93be030060e5fc12024a49d6" dependencies = [ "unicode-ident", ] @@ -705,18 +705,18 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "serde" -version = "1.0.202" +version = "1.0.203" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "226b61a0d411b2ba5ff6d7f73a476ac4f8bb900373459cd00fab8512828ba395" +checksum = "7253ab4de971e72fb7be983802300c30b5a7f0c2e56fab8abfc6a214307c0094" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.202" +version = "1.0.203" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6048858004bcff69094cd972ed40a32500f153bd3be9f716b2eed2e8217c4838" +checksum = "500cbc0ebeb6f46627f50f3f5811ccf6bf00643be300b4c3eabc0ef55dc5b5ba" dependencies = [ "proc-macro2", "quote", @@ -1099,9 +1099,9 @@ checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" [[package]] name = "winnow" -version = "0.6.8" +version = "0.6.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3c52e9c97a68071b23e836c9380edae937f17b9c4667bd021973efc689f618d" +checksum = "86c949fede1d13936a99f14fafd3e76fd642b556dd2ce96287fbe2e0151bfac6" dependencies = [ "memchr", ] diff --git a/Cargo.toml b/Cargo.toml index ebcbe6c..90e329e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,7 +20,7 @@ license = "MIT" edition = "2021" [workspace.dependencies] -serde = { version = "1.0.202", features = ["derive"] } +serde = { version = "1.0.203", features = ["derive"] } toml_edit = { version = "0.22.13", default-features = false, features = ["parse", "serde"] } [package] -- cgit v1.2.3 From 42bd0b8b75cbec30cb506f42295413ba0a6a1680 Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 6 Jun 2024 01:58:05 +0200 Subject: Update deps --- Cargo.lock | 38 ++++++++++++++++---------------------- Cargo.toml | 4 ++-- 2 files changed, 18 insertions(+), 24 deletions(-) (limited to 'Cargo.lock') diff --git a/Cargo.lock b/Cargo.lock index fec0365..fc37b23 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -61,9 +61,9 @@ dependencies = [ [[package]] name = "anstyle-query" -version = "1.0.3" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a64c907d4e79225ac72e2a354c9ce84d50ebb4586dee56c82b3ee73004f537f5" +checksum = "ad186efb764318d35165f1758e7dcef3b10628e26d41a44bc5550652e6804391" dependencies = [ "windows-sys 0.52.0", ] @@ -177,7 +177,7 @@ version = "4.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "528131438037fd55894f62d6e9f068b8f45ac57ffa77517819645d10aed04f64" dependencies = [ - "heck 0.5.0", + "heck", "proc-macro2", "quote", "syn", @@ -312,12 +312,6 @@ dependencies = [ "allocator-api2", ] -[[package]] -name = "heck" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" - [[package]] name = "heck" version = "0.5.0" @@ -496,9 +490,9 @@ checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" [[package]] name = "os_pipe" -version = "1.1.5" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57119c3b893986491ec9aa85056780d3a0f3cf4da7cc09dd3650dbd6c6738fb9" +checksum = "29d73ba8daf8fac13b0501d1abeddcfe21ba7401ada61a819144b6c2a4f32209" dependencies = [ "libc", "windows-sys 0.52.0", @@ -565,9 +559,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.84" +version = "1.0.85" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec96c6a92621310b51366f1e28d05ef11489516e93be030060e5fc12024a49d6" +checksum = "22244ce15aa966053a896d1accb3a6e68469b97c7f33f284b99f0d576879fc23" dependencies = [ "unicode-ident", ] @@ -812,11 +806,11 @@ dependencies = [ [[package]] name = "strum_macros" -version = "0.26.2" +version = "0.26.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6cf59daf282c0a494ba14fd21610a0325f9f90ec9d1231dea26bcb1d696c946" +checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be" dependencies = [ - "heck 0.4.1", + "heck", "proc-macro2", "quote", "rustversion", @@ -851,9 +845,9 @@ dependencies = [ [[package]] name = "toml_edit" -version = "0.22.13" +version = "0.22.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c127785850e8c20836d49732ae6abfa47616e60bf9d9f57c43c250361a9db96c" +checksum = "f21c7aaf97f1bd9ca9d4f9e73b0a6c74bd5afef56f2bc931943a6e1c37e04e38" dependencies = [ "indexmap", "serde", @@ -886,9 +880,9 @@ dependencies = [ [[package]] name = "unicode-width" -version = "0.1.12" +version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68f5e5f3158ecfd4b8ff6fe086db7c8467a2dfdac97fe420f2b7c4aa97af66d6" +checksum = "0336d538f7abc86d282a4189614dfaa90810dfc2c6f6427eaf88e16311dd225d" [[package]] name = "utf8parse" @@ -1099,9 +1093,9 @@ checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" [[package]] name = "winnow" -version = "0.6.9" +version = "0.6.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86c949fede1d13936a99f14fafd3e76fd642b556dd2ce96287fbe2e0151bfac6" +checksum = "56c52728401e1dc672a56e81e593e912aa54c78f40246869f78359a2bf24d29d" dependencies = [ "memchr", ] diff --git a/Cargo.toml b/Cargo.toml index 90e329e..8845027 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,7 +21,7 @@ edition = "2021" [workspace.dependencies] serde = { version = "1.0.203", features = ["derive"] } -toml_edit = { version = "0.22.13", default-features = false, features = ["parse", "serde"] } +toml_edit = { version = "0.22.14", default-features = false, features = ["parse", "serde"] } [package] name = "rustlings" @@ -51,7 +51,7 @@ clap = { version = "4.5.4", features = ["derive"] } crossterm = "0.27.0" hashbrown = "0.14.5" notify-debouncer-mini = { version = "0.4.1", default-features = false } -os_pipe = "1.1.5" +os_pipe = "1.2.0" ratatui = { version = "0.26.3", default-features = false, features = ["crossterm"] } rustlings-macros = { path = "rustlings-macros", version = "=6.0.0-beta.9" } serde_json = "1.0.117" -- cgit v1.2.3 From 0e4136d31e9aff282532b1a85cf40013bfba3f27 Mon Sep 17 00:00:00 2001 From: mo8it Date: Sat, 8 Jun 2024 21:19:16 +0200 Subject: Update deps --- Cargo.lock | 24 ++++++++++++------------ Cargo.toml | 2 +- 2 files changed, 13 insertions(+), 13 deletions(-) (limited to 'Cargo.lock') diff --git a/Cargo.lock b/Cargo.lock index fc37b23..58183ad 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -151,9 +151,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "clap" -version = "4.5.4" +version = "4.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90bc066a67923782aa8515dbaea16946c5bcc5addbd668bb80af688e53e548a0" +checksum = "a9689a29b593160de5bc4aacab7b5d54fb52231de70122626c178e6a368994c7" dependencies = [ "clap_builder", "clap_derive", @@ -161,9 +161,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.2" +version = "4.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae129e2e766ae0ec03484e609954119f123cc1fe650337e155d03b022f24f7b4" +checksum = "2e5387378c84f6faa26890ebf9f0a92989f8873d4d380467bcd0d8d8620424df" dependencies = [ "anstream", "anstyle", @@ -173,9 +173,9 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.4" +version = "4.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "528131438037fd55894f62d6e9f068b8f45ac57ffa77517819645d10aed04f64" +checksum = "c780290ccf4fb26629baa7a1081e68ced113f1d3ec302fa5948f1c381ebf06c6" dependencies = [ "heck", "proc-macro2", @@ -185,9 +185,9 @@ dependencies = [ [[package]] name = "clap_lex" -version = "0.7.0" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce" +checksum = "4b82cf0babdbd58558212896d1a4272303a57bdb245c2bf1147185fb45640e70" [[package]] name = "colorchoice" @@ -886,9 +886,9 @@ checksum = "0336d538f7abc86d282a4189614dfaa90810dfc2c6f6427eaf88e16311dd225d" [[package]] name = "utf8parse" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "version_check" @@ -1093,9 +1093,9 @@ checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" [[package]] name = "winnow" -version = "0.6.11" +version = "0.6.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56c52728401e1dc672a56e81e593e912aa54c78f40246869f78359a2bf24d29d" +checksum = "59b5e5f6c299a3c7890b876a2a587f3115162487e704907d9b6cd29473052ba1" dependencies = [ "memchr", ] diff --git a/Cargo.toml b/Cargo.toml index 8845027..6ca25d7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -47,7 +47,7 @@ include = [ [dependencies] anyhow = "1.0.86" -clap = { version = "4.5.4", features = ["derive"] } +clap = { version = "4.5.6", features = ["derive"] } crossterm = "0.27.0" hashbrown = "0.14.5" notify-debouncer-mini = { version = "0.4.1", default-features = false } -- cgit v1.2.3 From 2ff18137462513883533a10c929fe59364b8f884 Mon Sep 17 00:00:00 2001 From: mo8it Date: Fri, 14 Jun 2024 13:32:02 +0200 Subject: Update deps --- Cargo.lock | 24 ++++++++++++------------ Cargo.toml | 2 +- 2 files changed, 13 insertions(+), 13 deletions(-) (limited to 'Cargo.lock') diff --git a/Cargo.lock b/Cargo.lock index 58183ad..40b23f6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -151,9 +151,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "clap" -version = "4.5.6" +version = "4.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9689a29b593160de5bc4aacab7b5d54fb52231de70122626c178e6a368994c7" +checksum = "5db83dced34638ad474f39f250d7fea9598bdd239eaced1bdf45d597da0f433f" dependencies = [ "clap_builder", "clap_derive", @@ -161,9 +161,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.6" +version = "4.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e5387378c84f6faa26890ebf9f0a92989f8873d4d380467bcd0d8d8620424df" +checksum = "f7e204572485eb3fbf28f871612191521df159bc3e15a9f5064c66dba3a8c05f" dependencies = [ "anstream", "anstyle", @@ -422,9 +422,9 @@ dependencies = [ [[package]] name = "memchr" -version = "2.7.2" +version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" [[package]] name = "mio" @@ -615,9 +615,9 @@ dependencies = [ [[package]] name = "regex" -version = "1.10.4" +version = "1.10.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c117dbdfde9c8308975b6a18d71f3f385c89461f7b3fb054288ecf2a2058ba4c" +checksum = "b91213439dad192326a0d7c6ee3955910425f441d7038e0d6933b0aec5c4517f" dependencies = [ "aho-corasick", "memchr", @@ -627,9 +627,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.6" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea" +checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df" dependencies = [ "aho-corasick", "memchr", @@ -638,9 +638,9 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.8.3" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56" +checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" [[package]] name = "rustlings" diff --git a/Cargo.toml b/Cargo.toml index 6ca25d7..8e67312 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -47,7 +47,7 @@ include = [ [dependencies] anyhow = "1.0.86" -clap = { version = "4.5.6", features = ["derive"] } +clap = { version = "4.5.7", features = ["derive"] } crossterm = "0.27.0" hashbrown = "0.14.5" notify-debouncer-mini = { version = "0.4.1", default-features = false } -- cgit v1.2.3 From 2a1bc5377177981fa253ae4cb234bce584ce1b62 Mon Sep 17 00:00:00 2001 From: mo8it Date: Wed, 19 Jun 2024 14:03:06 +0200 Subject: Update deps --- Cargo.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'Cargo.lock') diff --git a/Cargo.lock b/Cargo.lock index 40b23f6..82c06f9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -516,7 +516,7 @@ checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" dependencies = [ "cfg-if", "libc", - "redox_syscall 0.5.1", + "redox_syscall 0.5.2", "smallvec", "windows-targets 0.52.5", ] @@ -606,9 +606,9 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.5.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "469052894dcb553421e483e4209ee581a45100d31b4018de03e5a7ad86374a7e" +checksum = "c82cf8cff14456045f55ec4241383baeff27af886adb72ffb2162f99911de0fd" dependencies = [ "bitflags 2.5.0", ] -- cgit v1.2.3 From 720b280bc1d8230821b4e6f2466eddb43245b8ff Mon Sep 17 00:00:00 2001 From: mo8it Date: Wed, 26 Jun 2024 16:59:13 +0200 Subject: Update deps --- Cargo.lock | 50 ++++++++++++++++++++++++++++++-------------------- Cargo.toml | 4 ++-- 2 files changed, 32 insertions(+), 22 deletions(-) (limited to 'Cargo.lock') diff --git a/Cargo.lock b/Cargo.lock index 82c06f9..f41ba86 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -113,9 +113,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.5.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" +checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" [[package]] name = "bstr" @@ -229,7 +229,7 @@ version = "0.27.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f476fe445d41c9e991fd07515a6f463074b782242ccf4a5b7b1d1012e70824df" dependencies = [ - "bitflags 2.5.0", + "bitflags 2.6.0", "crossterm_winapi", "libc", "mio", @@ -262,9 +262,9 @@ checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10" [[package]] name = "either" -version = "1.12.0" +version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3dca9240753cf90908d7e4aac30f630662b02aebaa1b58a3cadabdb23385b58b" +checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" [[package]] name = "equivalent" @@ -363,6 +363,15 @@ dependencies = [ "either", ] +[[package]] +name = "itertools" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" +dependencies = [ + "either", +] + [[package]] name = "itoa" version = "1.0.11" @@ -450,7 +459,7 @@ version = "6.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6205bd8bb1e454ad2e27422015fb5e4f2bcc7e08fa8f27058670d208324a4d2d" dependencies = [ - "bitflags 2.5.0", + "bitflags 2.6.0", "crossbeam-channel", "filetime", "fsevent-sys", @@ -559,9 +568,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.85" +version = "1.0.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22244ce15aa966053a896d1accb3a6e68469b97c7f33f284b99f0d576879fc23" +checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" dependencies = [ "unicode-ident", ] @@ -577,19 +586,20 @@ dependencies = [ [[package]] name = "ratatui" -version = "0.26.3" +version = "0.27.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f44c9e68fd46eda15c646fbb85e1040b657a58cdc8c98db1d97a55930d991eef" +checksum = "d16546c5b5962abf8ce6e2881e722b4e0ae3b6f1a08a26ae3573c55853ca68d3" dependencies = [ - "bitflags 2.5.0", + "bitflags 2.6.0", "cassowary", "compact_str", "crossterm", - "itertools", + "itertools 0.13.0", "lru", "paste", "stability", "strum", + "strum_macros", "unicode-segmentation", "unicode-truncate", "unicode-width", @@ -610,7 +620,7 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c82cf8cff14456045f55ec4241383baeff27af886adb72ffb2162f99911de0fd" dependencies = [ - "bitflags 2.5.0", + "bitflags 2.6.0", ] [[package]] @@ -719,9 +729,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.117" +version = "1.0.118" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "455182ea6142b14f93f4bc5320a2b31c1f266b66a4a5c858b013302a5d8cbfc3" +checksum = "d947f6b3163d8857ea16c4fa0dd4840d52f3041039a85decd46867eb1abef2e4" dependencies = [ "itoa", "ryu", @@ -797,9 +807,9 @@ checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" [[package]] name = "strum" -version = "0.26.2" +version = "0.26.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d8cec3501a5194c432b2b7976db6b7d10ec95c253208b45f83f7136aa985e29" +checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06" dependencies = [ "strum_macros", ] @@ -819,9 +829,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.66" +version = "2.0.68" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c42f3f41a2de00b01c0aaad383c5a45241efc8b2d1eda5661812fda5f3cdcff5" +checksum = "901fa70d88b9d6c98022e23b4136f9f3e54e4662c3bc1bd1d84a42a9a0f0c1e9" dependencies = [ "proc-macro2", "quote", @@ -874,7 +884,7 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a5fbabedabe362c618c714dbefda9927b5afc8e2a8102f47f081089a9019226" dependencies = [ - "itertools", + "itertools 0.12.1", "unicode-width", ] diff --git a/Cargo.toml b/Cargo.toml index 8e67312..64844c3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -52,9 +52,9 @@ crossterm = "0.27.0" hashbrown = "0.14.5" notify-debouncer-mini = { version = "0.4.1", default-features = false } os_pipe = "1.2.0" -ratatui = { version = "0.26.3", default-features = false, features = ["crossterm"] } +ratatui = { version = "0.27.0", default-features = false, features = ["crossterm"] } rustlings-macros = { path = "rustlings-macros", version = "=6.0.0-beta.9" } -serde_json = "1.0.117" +serde_json = "1.0.118" serde.workspace = true toml_edit.workspace = true -- cgit v1.2.3 From bcebbb9df6987cd21ecf615907d5d06f8b626edc Mon Sep 17 00:00:00 2001 From: mo8it Date: Tue, 2 Jul 2024 01:45:55 +0200 Subject: Update deps --- Cargo.lock | 20 ++++++++++---------- Cargo.toml | 4 ++-- 2 files changed, 12 insertions(+), 12 deletions(-) (limited to 'Cargo.lock') diff --git a/Cargo.lock b/Cargo.lock index f41ba86..618e422 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -151,9 +151,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "clap" -version = "4.5.7" +version = "4.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5db83dced34638ad474f39f250d7fea9598bdd239eaced1bdf45d597da0f433f" +checksum = "84b3edb18336f4df585bc9aa31dd99c036dfa5dc5e9a2939a722a188f3a8970d" dependencies = [ "clap_builder", "clap_derive", @@ -161,9 +161,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.7" +version = "4.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7e204572485eb3fbf28f871612191521df159bc3e15a9f5064c66dba3a8c05f" +checksum = "c1c09dd5ada6c6c78075d6fd0da3f90d8080651e2d6cc8eb2f1aaa4034ced708" dependencies = [ "anstream", "anstyle", @@ -173,9 +173,9 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.5" +version = "4.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c780290ccf4fb26629baa7a1081e68ced113f1d3ec302fa5948f1c381ebf06c6" +checksum = "2bac35c6dafb060fd4d275d9a4ffae97917c13a6327903a8be2153cd964f7085" dependencies = [ "heck", "proc-macro2", @@ -416,9 +416,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.21" +version = "0.4.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" +checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" [[package]] name = "lru" @@ -729,9 +729,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.118" +version = "1.0.120" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d947f6b3163d8857ea16c4fa0dd4840d52f3041039a85decd46867eb1abef2e4" +checksum = "4e0d21c9a8cae1235ad58a00c11cb40d4b1e5c784f1ef2c537876ed6ffd8b7c5" dependencies = [ "itoa", "ryu", diff --git a/Cargo.toml b/Cargo.toml index 64844c3..97f1c19 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -47,14 +47,14 @@ include = [ [dependencies] anyhow = "1.0.86" -clap = { version = "4.5.7", features = ["derive"] } +clap = { version = "4.5.8", features = ["derive"] } crossterm = "0.27.0" hashbrown = "0.14.5" notify-debouncer-mini = { version = "0.4.1", default-features = false } os_pipe = "1.2.0" ratatui = { version = "0.27.0", default-features = false, features = ["crossterm"] } rustlings-macros = { path = "rustlings-macros", version = "=6.0.0-beta.9" } -serde_json = "1.0.118" +serde_json = "1.0.120" serde.workspace = true toml_edit.workspace = true -- cgit v1.2.3 From 2d792651ea5d500537e23d2825b919cff769d30b Mon Sep 17 00:00:00 2001 From: mo8it Date: Tue, 2 Jul 2024 14:29:07 +0200 Subject: chore: Release --- Cargo.lock | 4 ++-- Cargo.toml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'Cargo.lock') diff --git a/Cargo.lock b/Cargo.lock index 618e422..f5908cc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -654,7 +654,7 @@ checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" [[package]] name = "rustlings" -version = "6.0.0-beta.9" +version = "6.0.0-beta.10" dependencies = [ "anyhow", "assert_cmd", @@ -673,7 +673,7 @@ dependencies = [ [[package]] name = "rustlings-macros" -version = "6.0.0-beta.9" +version = "6.0.0-beta.10" dependencies = [ "quote", "serde", diff --git a/Cargo.toml b/Cargo.toml index 97f1c19..3455e2b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,7 +8,7 @@ exclude = [ ] [workspace.package] -version = "6.0.0-beta.9" +version = "6.0.0-beta.10" authors = [ "Liv ", "Mo Bitar ", @@ -53,7 +53,7 @@ hashbrown = "0.14.5" notify-debouncer-mini = { version = "0.4.1", default-features = false } os_pipe = "1.2.0" ratatui = { version = "0.27.0", default-features = false, features = ["crossterm"] } -rustlings-macros = { path = "rustlings-macros", version = "=6.0.0-beta.9" } +rustlings-macros = { path = "rustlings-macros", version = "=6.0.0-beta.10" } serde_json = "1.0.120" serde.workspace = true toml_edit.workspace = true -- cgit v1.2.3