mirror of
https://github.com/rust-lang/rustlings.git
synced 2024-12-27 00:00:03 +03:00
Compare commits
19 commits
4ce8667b9d
...
ce3dcc9856
Author | SHA1 | Date | |
---|---|---|---|
ce3dcc9856 | |||
4472d50eba | |||
a1d5702ba0 | |||
52a231ce2f | |||
16af981772 | |||
fc141b8dfc | |||
82ebd29ff6 | |||
f5737b5a49 | |||
55e68d2c63 | |||
479f45da9b | |||
140c4e4812 | |||
337460d299 | |||
e41c3a7c92 | |||
1b9faa4d61 | |||
9f9a754a64 | |||
f7b0cfe8d1 | |||
8b43d79257 | |||
dc086c6bf1 | |||
dc0ffbe16e |
13
CHANGELOG.md
13
CHANGELOG.md
|
@ -1,13 +1,20 @@
|
|||
<a name="6.2.0"></a>
|
||||
|
||||
## 6.2.0 (2024-08-08)
|
||||
## 6.2.0 (2024-08-09)
|
||||
|
||||
### Added
|
||||
|
||||
- Show a message before checking and running an exercise. This gives the user instant feedback and avoids confusion if the checks take too long.
|
||||
- Show a helpful error message when trying to install Rustlings with a Rust version lower than the minimum one that Rustlings supports.
|
||||
- Remove the state file and the solutions directory from the generated `.gitignore` file.
|
||||
- Add a `README.md` file to the `solutions/` directory.
|
||||
- Allow initializing Rustlings in a Cargo workspace.
|
||||
- `dev check`: Check that all solutions are formatted with `rustfmt`.
|
||||
|
||||
### Changed
|
||||
|
||||
- Remove the state file and the solutions directory from the generated `.gitignore` file.
|
||||
- Run the final check of all exercises in parallel.
|
||||
- Small exercise improvements.
|
||||
- `dev check`: Check that all solutions are formatted with `rustfmt`.
|
||||
|
||||
<a name="6.1.0"></a>
|
||||
|
||||
|
|
135
Cargo.lock
generated
135
Cargo.lock
generated
|
@ -116,9 +116,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
|||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "4.5.13"
|
||||
version = "4.5.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0fbb260a053428790f3de475e304ff84cdbc4face759ea7a3e64c1edd938a7fc"
|
||||
checksum = "c937d4061031a6d0c8da4b9a4f98a172fc2976dfb1c19213a9cf7d0d3c837e36"
|
||||
dependencies = [
|
||||
"clap_builder",
|
||||
"clap_derive",
|
||||
|
@ -126,9 +126,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "clap_builder"
|
||||
version = "4.5.13"
|
||||
version = "4.5.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "64b17d7ea74e9f833c7dbf2cbe4fb12ff26783eda4782a8975b72f895c9b4d99"
|
||||
checksum = "85379ba512b21a328adf887e85f7742d12e96eb31f3ef077df4ffc26b506ffed"
|
||||
dependencies = [
|
||||
"anstream",
|
||||
"anstyle",
|
||||
|
@ -162,13 +162,14 @@ checksum = "d3fd119d74b830634cea2a0f58bbd0d54540518a14397557951e79340abc28c0"
|
|||
|
||||
[[package]]
|
||||
name = "compact_str"
|
||||
version = "0.7.1"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f86b9c4c00838774a6d902ef931eff7470720c51d90c2e32cfe15dc304737b3f"
|
||||
checksum = "6050c3a16ddab2e412160b31f2c871015704239bca62f72f6e5f0be631d3f644"
|
||||
dependencies = [
|
||||
"castaway",
|
||||
"cfg-if",
|
||||
"itoa",
|
||||
"rustversion",
|
||||
"ryu",
|
||||
"static_assertions",
|
||||
]
|
||||
|
@ -190,15 +191,15 @@ checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80"
|
|||
|
||||
[[package]]
|
||||
name = "crossterm"
|
||||
version = "0.27.0"
|
||||
version = "0.28.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f476fe445d41c9e991fd07515a6f463074b782242ccf4a5b7b1d1012e70824df"
|
||||
checksum = "829d955a0bb380ef178a640b91779e3987da38c9aea133b20614cfed8cdea9c6"
|
||||
dependencies = [
|
||||
"bitflags 2.6.0",
|
||||
"crossterm_winapi",
|
||||
"libc",
|
||||
"mio",
|
||||
"mio 1.0.1",
|
||||
"parking_lot",
|
||||
"rustix",
|
||||
"signal-hook",
|
||||
"signal-hook-mio",
|
||||
"winapi",
|
||||
|
@ -225,6 +226,22 @@ version = "1.0.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
|
||||
|
||||
[[package]]
|
||||
name = "errno"
|
||||
version = "0.3.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fastrand"
|
||||
version = "2.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9fc0510504f03c51ada170672ac806f1f105a88aa97a5281117e1ddc3368e51a"
|
||||
|
||||
[[package]]
|
||||
name = "filetime"
|
||||
version = "0.2.23"
|
||||
|
@ -262,6 +279,12 @@ version = "0.5.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
|
||||
|
||||
[[package]]
|
||||
name = "hermit-abi"
|
||||
version = "0.3.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024"
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "2.3.0"
|
||||
|
@ -292,6 +315,16 @@ dependencies = [
|
|||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "instability"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b23a0c8dfe501baac4adf6ebbfa6eddf8f0c07f56b058cc1288017e32397846c"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "is_terminal_polyfill"
|
||||
version = "1.70.1"
|
||||
|
@ -339,6 +372,12 @@ version = "0.2.155"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c"
|
||||
|
||||
[[package]]
|
||||
name = "linux-raw-sys"
|
||||
version = "0.4.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89"
|
||||
|
||||
[[package]]
|
||||
name = "lock_api"
|
||||
version = "0.4.12"
|
||||
|
@ -382,6 +421,19 @@ dependencies = [
|
|||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mio"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4569e456d394deccd22ce1c1913e6ea0e54519f577285001215d33557431afe4"
|
||||
dependencies = [
|
||||
"hermit-abi",
|
||||
"libc",
|
||||
"log",
|
||||
"wasi",
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "notify"
|
||||
version = "6.1.1"
|
||||
|
@ -396,7 +448,7 @@ dependencies = [
|
|||
"kqueue",
|
||||
"libc",
|
||||
"log",
|
||||
"mio",
|
||||
"mio 0.8.11",
|
||||
"walkdir",
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
@ -476,18 +528,18 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "ratatui"
|
||||
version = "0.27.0"
|
||||
version = "0.28.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d16546c5b5962abf8ce6e2881e722b4e0ae3b6f1a08a26ae3573c55853ca68d3"
|
||||
checksum = "5ba6a365afbe5615999275bea2446b970b10a41102500e27ce7678d50d978303"
|
||||
dependencies = [
|
||||
"bitflags 2.6.0",
|
||||
"cassowary",
|
||||
"compact_str",
|
||||
"crossterm",
|
||||
"instability",
|
||||
"itertools",
|
||||
"lru",
|
||||
"paste",
|
||||
"stability",
|
||||
"strum",
|
||||
"strum_macros",
|
||||
"unicode-segmentation",
|
||||
|
@ -514,24 +566,38 @@ dependencies = [
|
|||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustlings"
|
||||
version = "6.1.0"
|
||||
name = "rustix"
|
||||
version = "0.38.34"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f"
|
||||
dependencies = [
|
||||
"bitflags 2.6.0",
|
||||
"errno",
|
||||
"libc",
|
||||
"linux-raw-sys",
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustlings"
|
||||
version = "6.2.0"
|
||||
dependencies = [
|
||||
"ahash",
|
||||
"anyhow",
|
||||
"clap",
|
||||
"hashbrown",
|
||||
"notify-debouncer-mini",
|
||||
"os_pipe",
|
||||
"ratatui",
|
||||
"rustlings-macros",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"tempfile",
|
||||
"toml_edit",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustlings-macros"
|
||||
version = "6.1.0"
|
||||
version = "6.2.0"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"serde",
|
||||
|
@ -567,18 +633,18 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
|
|||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.204"
|
||||
version = "1.0.205"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bc76f558e0cbb2a839d37354c575f1dc3fdc6546b5be373ba43d95f231bf7c12"
|
||||
checksum = "e33aedb1a7135da52b7c21791455563facbbcc43d0f0f66165b42c21b3dfb150"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.204"
|
||||
version = "1.0.205"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e0cd7e117be63d3c3678776753929474f3b04a43a080c744d6b0ae2a8c28e222"
|
||||
checksum = "692d6f5ac90220161d6774db30c662202721e64aed9058d2c394f451261420c1"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
@ -623,7 +689,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "34db1a06d485c9142248b7a054f034b349b212551f3dfd19c94d45a754a217cd"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"mio",
|
||||
"mio 1.0.1",
|
||||
"signal-hook",
|
||||
]
|
||||
|
||||
|
@ -642,16 +708,6 @@ version = "1.13.2"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67"
|
||||
|
||||
[[package]]
|
||||
name = "stability"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d904e7009df136af5297832a3ace3370cd14ff1546a232f4f185036c2736fcac"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "static_assertions"
|
||||
version = "1.1.0"
|
||||
|
@ -697,6 +753,19 @@ dependencies = [
|
|||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tempfile"
|
||||
version = "3.12.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "04cbcdd0c794ebb0d4cf35e88edd2f7d2c4c3e9a5a6dab322839b321c6a87a64"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"fastrand",
|
||||
"once_cell",
|
||||
"rustix",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml_datetime"
|
||||
version = "0.6.8"
|
||||
|
|
15
Cargo.toml
15
Cargo.toml
|
@ -6,7 +6,7 @@ exclude = [
|
|||
]
|
||||
|
||||
[workspace.package]
|
||||
version = "6.1.0"
|
||||
version = "6.2.0"
|
||||
authors = [
|
||||
"Mo Bitar <mo8it@proton.me>", # https://github.com/mo8it
|
||||
"Liv <mokou@fastmail.com>", # https://github.com/shadows-withal
|
||||
|
@ -19,7 +19,7 @@ edition = "2021" # On Update: Update the edition of the `rustfmt` command that c
|
|||
rust-version = "1.80"
|
||||
|
||||
[workspace.dependencies]
|
||||
serde = { version = "1.0.204", features = ["derive"] }
|
||||
serde = { version = "1.0.205", features = ["derive"] }
|
||||
toml_edit = { version = "0.22.20", default-features = false, features = ["parse", "serde"] }
|
||||
|
||||
[package]
|
||||
|
@ -46,17 +46,20 @@ include = [
|
|||
]
|
||||
|
||||
[dependencies]
|
||||
ahash = { version = "0.8.11", default-features = false }
|
||||
anyhow = "1.0.86"
|
||||
clap = { version = "4.5.13", features = ["derive"] }
|
||||
hashbrown = "0.14.5"
|
||||
clap = { version = "4.5.14", features = ["derive"] }
|
||||
notify-debouncer-mini = { version = "0.4.1", default-features = false }
|
||||
os_pipe = "1.2.1"
|
||||
ratatui = { version = "0.27.0", default-features = false, features = ["crossterm"] }
|
||||
rustlings-macros = { path = "rustlings-macros", version = "=6.1.0" }
|
||||
ratatui = { version = "0.28.0", default-features = false, features = ["crossterm"] }
|
||||
rustlings-macros = { path = "rustlings-macros", version = "=6.2.0" }
|
||||
serde_json = "1.0.122"
|
||||
serde.workspace = true
|
||||
toml_edit.workspace = true
|
||||
|
||||
[dev-dependencies]
|
||||
tempfile = "3.12.0"
|
||||
|
||||
[profile.release]
|
||||
panic = "abort"
|
||||
|
||||
|
|
|
@ -3,4 +3,4 @@
|
|||
Before you finish an exercise, its solution file will only contain an empty `main` function.
|
||||
The content of this file will be automatically replaced by the actual solution once you finish the exercise.
|
||||
|
||||
Note that these solution are often only _one possibility_ to solve an exercise.
|
||||
Note that these solutions are often only _one possibility_ to solve an exercise.
|
||||
|
|
|
@ -10,6 +10,7 @@ use std::{
|
|||
use crate::{
|
||||
clear_terminal,
|
||||
cmd::CmdRunner,
|
||||
collections::hash_set_with_capacity,
|
||||
embedded::EMBEDDED_FILES,
|
||||
exercise::{Exercise, RunnableExercise},
|
||||
info_file::ExerciseInfo,
|
||||
|
@ -69,7 +70,7 @@ impl AppState {
|
|||
return StateFileStatus::NotRead;
|
||||
}
|
||||
|
||||
let mut done_exercises = hashbrown::HashSet::with_capacity(self.exercises.len());
|
||||
let mut done_exercises = hash_set_with_capacity(self.exercises.len());
|
||||
|
||||
for done_exerise_name in lines {
|
||||
if done_exerise_name.is_empty() {
|
||||
|
|
16
src/cmd.rs
16
src/cmd.rs
|
@ -1,4 +1,4 @@
|
|||
use anyhow::{Context, Result};
|
||||
use anyhow::{bail, Context, Result};
|
||||
use serde::Deserialize;
|
||||
use std::{
|
||||
io::Read,
|
||||
|
@ -68,12 +68,16 @@ impl CmdRunner {
|
|||
.stdin(Stdio::null())
|
||||
.stderr(Stdio::inherit())
|
||||
.output()
|
||||
.context(CARGO_METADATA_ERR)?
|
||||
.stdout;
|
||||
.context(CARGO_METADATA_ERR)?;
|
||||
|
||||
let target_dir = serde_json::de::from_slice::<CargoMetadata>(&metadata_output)
|
||||
.context("Failed to read the field `target_directory` from the `cargo metadata` output")
|
||||
.map(|metadata| metadata.target_directory)?;
|
||||
if !metadata_output.status.success() {
|
||||
bail!("The command `cargo metadata …` failed. Are you in the `rustlings/` directory?");
|
||||
}
|
||||
|
||||
let target_dir = serde_json::de::from_slice::<CargoMetadata>(&metadata_output.stdout)
|
||||
.context(
|
||||
"Failed to read the field `target_directory` from the output of the command `cargo metadata …`",
|
||||
)?.target_directory;
|
||||
|
||||
Ok(Self { target_dir })
|
||||
}
|
||||
|
|
10
src/collections.rs
Normal file
10
src/collections.rs
Normal file
|
@ -0,0 +1,10 @@
|
|||
use ahash::AHasher;
|
||||
use std::hash::BuildHasherDefault;
|
||||
|
||||
/// DOS attacks aren't a concern for Rustlings. Therefore, we use `ahash` with fixed seeds.
|
||||
pub type HashSet<T> = std::collections::HashSet<T, BuildHasherDefault<AHasher>>;
|
||||
|
||||
#[inline]
|
||||
pub fn hash_set_with_capacity<T>(capacity: usize) -> HashSet<T> {
|
||||
HashSet::with_capacity_and_hasher(capacity, BuildHasherDefault::<AHasher>::default())
|
||||
}
|
|
@ -11,6 +11,7 @@ use std::{
|
|||
use crate::{
|
||||
cargo_toml::{append_bins, bins_start_end_ind, BINS_BUFFER_CAPACITY},
|
||||
cmd::CmdRunner,
|
||||
collections::{hash_set_with_capacity, HashSet},
|
||||
exercise::{RunnableExercise, OUTPUT_CAPACITY},
|
||||
info_file::{ExerciseInfo, InfoFile},
|
||||
CURRENT_FORMAT_VERSION,
|
||||
|
@ -48,9 +49,9 @@ fn check_cargo_toml(
|
|||
}
|
||||
|
||||
// Check the info of all exercises and return their paths in a set.
|
||||
fn check_info_file_exercises(info_file: &InfoFile) -> Result<hashbrown::HashSet<PathBuf>> {
|
||||
let mut names = hashbrown::HashSet::with_capacity(info_file.exercises.len());
|
||||
let mut paths = hashbrown::HashSet::with_capacity(info_file.exercises.len());
|
||||
fn check_info_file_exercises(info_file: &InfoFile) -> Result<HashSet<PathBuf>> {
|
||||
let mut names = hash_set_with_capacity(info_file.exercises.len());
|
||||
let mut paths = hash_set_with_capacity(info_file.exercises.len());
|
||||
|
||||
let mut file_buf = String::with_capacity(1 << 14);
|
||||
for exercise_info in &info_file.exercises {
|
||||
|
@ -111,10 +112,7 @@ fn check_info_file_exercises(info_file: &InfoFile) -> Result<hashbrown::HashSet<
|
|||
// Check `dir` for unexpected files.
|
||||
// Only Rust files in `allowed_rust_files` and `README.md` files are allowed.
|
||||
// Only one level of directory nesting is allowed.
|
||||
fn check_unexpected_files(
|
||||
dir: &str,
|
||||
allowed_rust_files: &hashbrown::HashSet<PathBuf>,
|
||||
) -> Result<()> {
|
||||
fn check_unexpected_files(dir: &str, allowed_rust_files: &HashSet<PathBuf>) -> Result<()> {
|
||||
let unexpected_file = |path: &Path| {
|
||||
anyhow!("Found the file `{}`. Only `README.md` and Rust files related to an exercise in `info.toml` are allowed in the `{dir}` directory", path.display())
|
||||
};
|
||||
|
@ -253,7 +251,7 @@ fn check_solutions(
|
|||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let mut sol_paths = hashbrown::HashSet::with_capacity(info_file.exercises.len());
|
||||
let mut sol_paths = hash_set_with_capacity(info_file.exercises.len());
|
||||
let mut fmt_cmd = Command::new("rustfmt");
|
||||
fmt_cmd
|
||||
.arg("--check")
|
||||
|
|
85
src/init.rs
85
src/init.rs
|
@ -1,10 +1,11 @@
|
|||
use anyhow::{bail, Context, Result};
|
||||
use ratatui::crossterm::style::Stylize;
|
||||
use serde::Deserialize;
|
||||
use std::{
|
||||
env::set_current_dir,
|
||||
fs::{self, create_dir},
|
||||
io::{self, Write},
|
||||
path::Path,
|
||||
path::{Path, PathBuf},
|
||||
process::{Command, Stdio},
|
||||
};
|
||||
|
||||
|
@ -13,27 +14,75 @@ use crate::{
|
|||
term::press_enter_prompt,
|
||||
};
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct CargoLocateProject {
|
||||
root: PathBuf,
|
||||
}
|
||||
|
||||
pub fn init() -> Result<()> {
|
||||
let rustlings_dir = Path::new("rustlings");
|
||||
if rustlings_dir.exists() {
|
||||
bail!(RUSTLINGS_DIR_ALREADY_EXISTS_ERR);
|
||||
}
|
||||
|
||||
let locate_project_output = Command::new("cargo")
|
||||
.arg("locate-project")
|
||||
.arg("-q")
|
||||
.arg("--workspace")
|
||||
.stdin(Stdio::null())
|
||||
.stderr(Stdio::null())
|
||||
.output()
|
||||
.context(CARGO_LOCATE_PROJECT_ERR)?;
|
||||
|
||||
let mut stdout = io::stdout().lock();
|
||||
let mut init_git = true;
|
||||
|
||||
if Path::new("Cargo.toml").exists() {
|
||||
if locate_project_output.status.success() {
|
||||
if Path::new("exercises").exists() && Path::new("solutions").exists() {
|
||||
bail!(IN_INITIALIZED_DIR_ERR);
|
||||
}
|
||||
|
||||
stdout.write_all(CARGO_TOML_EXISTS_PROMPT_MSG)?;
|
||||
press_enter_prompt(&mut stdout)?;
|
||||
init_git = false;
|
||||
}
|
||||
let workspace_manifest =
|
||||
serde_json::de::from_slice::<CargoLocateProject>(&locate_project_output.stdout)
|
||||
.context(
|
||||
"Failed to read the field `root` from the output of `cargo locate-project …`",
|
||||
)?
|
||||
.root;
|
||||
|
||||
stdout.write_all(b"This command will create the directory `rustlings/` which will contain the exercises.\nPress ENTER to continue ")?;
|
||||
press_enter_prompt(&mut stdout)?;
|
||||
let workspace_manifest_content = fs::read_to_string(&workspace_manifest)
|
||||
.with_context(|| format!("Failed to read the file {}", workspace_manifest.display()))?;
|
||||
if !workspace_manifest_content.contains("[workspace]\n")
|
||||
&& !workspace_manifest_content.contains("workspace.")
|
||||
{
|
||||
bail!("The current directory is already part of a Cargo project.\nPlease initialize Rustlings in a different directory");
|
||||
}
|
||||
|
||||
stdout.write_all(b"This command will create the directory `rustlings/` as a member of this Cargo workspace.\nPress ENTER to continue ")?;
|
||||
press_enter_prompt(&mut stdout)?;
|
||||
|
||||
// Make sure "rustlings" is added to `workspace.members` by making
|
||||
// Cargo initialize a new project.
|
||||
let status = Command::new("cargo")
|
||||
.arg("new")
|
||||
.arg("-q")
|
||||
.arg("--vcs")
|
||||
.arg("none")
|
||||
.arg("rustlings")
|
||||
.stdin(Stdio::null())
|
||||
.stdout(Stdio::null())
|
||||
.status()?;
|
||||
if !status.success() {
|
||||
bail!("Failed to initialize a new Cargo workspace member.\nPlease initialize Rustlings in a different directory");
|
||||
}
|
||||
|
||||
stdout.write_all(b"The directory `rustlings` has been added to `workspace.members` in the `Cargo.toml` file of this Cargo workspace.\n")?;
|
||||
fs::remove_dir_all("rustlings")
|
||||
.context("Failed to remove the temporary directory `rustlings/`")?;
|
||||
init_git = false;
|
||||
} else {
|
||||
stdout.write_all(b"This command will create the directory `rustlings/` which will contain the exercises.\nPress ENTER to continue ")?;
|
||||
press_enter_prompt(&mut stdout)?;
|
||||
}
|
||||
|
||||
create_dir(rustlings_dir).context("Failed to create the `rustlings/` directory")?;
|
||||
set_current_dir(rustlings_dir)
|
||||
|
@ -104,6 +153,10 @@ pub fn init() -> Result<()> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
const CARGO_LOCATE_PROJECT_ERR: &str = "Failed to run the command `cargo locate-project …`
|
||||
Did you already install Rust?
|
||||
Try running `cargo --version` to diagnose the problem.";
|
||||
|
||||
const INIT_SOLUTION_FILE: &[u8] = b"fn main() {
|
||||
// DON'T EDIT THIS SOLUTION FILE!
|
||||
// It will be automatically filled after you finish the exercise.
|
||||
|
@ -120,7 +173,7 @@ pub const VS_CODE_EXTENSIONS_JSON: &[u8] = br#"{"recommendations":["rust-lang.ru
|
|||
const IN_INITIALIZED_DIR_ERR: &str = "It looks like Rustlings is already initialized in this directory.
|
||||
|
||||
If you already initialized Rustlings, run the command `rustlings` for instructions on getting started with the exercises.
|
||||
Otherwise, please run `rustlings init` again in another directory.";
|
||||
Otherwise, please run `rustlings init` again in a different directory.";
|
||||
|
||||
const RUSTLINGS_DIR_ALREADY_EXISTS_ERR: &str =
|
||||
"A directory with the name `rustlings` already exists in the current directory.
|
||||
|
@ -128,19 +181,5 @@ You probably already initialized Rustlings.
|
|||
Run `cd rustlings`
|
||||
Then run `rustlings` again";
|
||||
|
||||
const CARGO_TOML_EXISTS_PROMPT_MSG: &[u8] = br#"You are about to initialize Rustlings in a directory that already contains a `Cargo.toml` file!
|
||||
|
||||
=> It is recommended to abort with CTRL+C and initialize Rustlings in another directory <=
|
||||
|
||||
If you know what you are doing and want to initialize Rustlings in a Cargo workspace,
|
||||
then you need to add its directory to `members` in the `workspace` section of the `Cargo.toml` file:
|
||||
|
||||
```toml
|
||||
[workspace]
|
||||
members = ["rustlings"]
|
||||
```
|
||||
|
||||
Press ENTER if you are sure that you want to continue after reading the warning above "#;
|
||||
|
||||
const POST_INIT_MSG: &str = "Run `cd rustlings` to go into the generated directory.
|
||||
Then run `rustlings` to get started.";
|
||||
|
|
|
@ -27,7 +27,7 @@ pub fn list(app_state: &mut AppState) -> Result<()> {
|
|||
let mut ui_state = UiState::new(app_state);
|
||||
|
||||
'outer: loop {
|
||||
terminal.draw(|frame| ui_state.draw(frame).unwrap())?;
|
||||
terminal.try_draw(|frame| ui_state.draw(frame).map_err(io::Error::other))?;
|
||||
|
||||
let key = loop {
|
||||
match event::read()? {
|
||||
|
|
|
@ -161,7 +161,7 @@ impl<'a> UiState<'a> {
|
|||
}
|
||||
|
||||
pub fn draw(&mut self, frame: &mut Frame) -> Result<()> {
|
||||
let area = frame.size();
|
||||
let area = frame.area();
|
||||
|
||||
frame.render_stateful_widget(
|
||||
&self.table,
|
||||
|
|
|
@ -13,6 +13,7 @@ use self::{app_state::AppState, dev::DevCommands, info_file::InfoFile, watch::Wa
|
|||
mod app_state;
|
||||
mod cargo_toml;
|
||||
mod cmd;
|
||||
mod collections;
|
||||
mod dev;
|
||||
mod embedded;
|
||||
mod exercise;
|
||||
|
@ -105,7 +106,7 @@ fn main() -> Result<()> {
|
|||
write!(stdout, "{welcome_message}\n\nPress ENTER to continue ")?;
|
||||
press_enter_prompt(&mut stdout)?;
|
||||
clear_terminal(&mut stdout)?;
|
||||
// Flush to be able to show errors occuring before printing a newline to stdout.
|
||||
// Flush to be able to show errors occurring before printing a newline to stdout.
|
||||
stdout.flush()?;
|
||||
}
|
||||
StateFileStatus::Read => (),
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
use std::{
|
||||
env::{self, consts::EXE_SUFFIX},
|
||||
fs,
|
||||
process::{Command, Stdio},
|
||||
str::from_utf8,
|
||||
};
|
||||
|
@ -155,28 +154,29 @@ fn hint() {
|
|||
|
||||
#[test]
|
||||
fn init() {
|
||||
let _ = fs::remove_dir_all("tests/rustlings");
|
||||
let test_dir = tempfile::TempDir::new().unwrap();
|
||||
let test_dir = test_dir.path().to_str().unwrap();
|
||||
|
||||
Cmd::default().current_dir("tests").fail();
|
||||
Cmd::default().current_dir(test_dir).fail();
|
||||
|
||||
Cmd::default()
|
||||
.current_dir("tests")
|
||||
.current_dir(test_dir)
|
||||
.args(&["init"])
|
||||
.success();
|
||||
|
||||
// Running `init` after a successful initialization.
|
||||
Cmd::default()
|
||||
.current_dir("tests")
|
||||
.current_dir(test_dir)
|
||||
.args(&["init"])
|
||||
.output(PartialStderr("`cd rustlings`"))
|
||||
.fail();
|
||||
|
||||
let initialized_dir = format!("{test_dir}/rustlings");
|
||||
|
||||
// Running `init` in the initialized directory.
|
||||
Cmd::default()
|
||||
.current_dir("tests/rustlings")
|
||||
.current_dir(&initialized_dir)
|
||||
.args(&["init"])
|
||||
.output(PartialStderr("already initialized"))
|
||||
.fail();
|
||||
|
||||
fs::remove_dir_all("tests/rustlings").unwrap();
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue