mirror of
https://github.com/rust-lang/rustlings.git
synced 2025-01-09 20:03:24 +03:00
Compare commits
No commits in common. "86684b7fc9dd5e8bedad6056565645d1d980925c" and "a2be6754bf2833371fe99ddc2d01c0d518f8eb27" have entirely different histories.
86684b7fc9
...
a2be6754bf
4
.gitignore
vendored
4
.gitignore
vendored
|
@ -1,7 +1,7 @@
|
|||
# Cargo
|
||||
target/
|
||||
Cargo.lock
|
||||
!/Cargo.lock
|
||||
/tests/fixture/*/Cargo.lock
|
||||
/dev/Cargo.lock
|
||||
|
||||
# State file
|
||||
.rustlings-state.txt
|
||||
|
|
16
Cargo.lock
generated
16
Cargo.lock
generated
|
@ -656,9 +656,9 @@ checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56"
|
|||
|
||||
[[package]]
|
||||
name = "rustix"
|
||||
version = "0.38.33"
|
||||
version = "0.38.32"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e3cc72858054fcff6d7dea32df2aeaee6a7c24227366d7ea429aada2f26b16ad"
|
||||
checksum = "65e04861e65f21776e67888bfbea442b3642beaa0138fdb1dd7a84a52dffdb89"
|
||||
dependencies = [
|
||||
"bitflags 2.5.0",
|
||||
"errno",
|
||||
|
@ -771,9 +771,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "signal-hook-registry"
|
||||
version = "1.4.2"
|
||||
version = "1.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1"
|
||||
checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
@ -830,9 +830,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.60"
|
||||
version = "2.0.59"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "909518bc7b1c9b779f1bbf07f2929d35af9f0f37e47c6e9ef7f9dddc1e1821f3"
|
||||
checksum = "4a6531ffc7b071655e4ce2e04bd464c4830bb585a61cabb96cf808f05172615a"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
@ -856,9 +856,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "toml_edit"
|
||||
version = "0.22.12"
|
||||
version = "0.22.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d3328d4f68a705b2a4498da1d580585d39a6510f98318a2cec3018a7ec61ddef"
|
||||
checksum = "8e40bb779c5187258fd7aad0eb68cb8706a0a81fa712fbea808ab43c4b8374c4"
|
||||
dependencies = [
|
||||
"indexmap",
|
||||
"serde",
|
||||
|
|
|
@ -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.12", default-features = false, features = ["parse", "serde"] }
|
||||
toml_edit = { version = "0.22.9", default-features = false, features = ["parse", "serde"] }
|
||||
which = "6.0.1"
|
||||
|
||||
[dev-dependencies]
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
# Don't edit the `bin` list manually! It is updated by `cargo run -- dev update`. This comment line will be stripped in `rustlings init`.
|
||||
# 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" },
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
This file is used to check if the user tries to run Rustlings in the repository (the method before v6)
|
|
@ -53,8 +53,7 @@ impl AppState {
|
|||
}
|
||||
|
||||
// See `Self::write` for more information about the file format.
|
||||
let mut lines = self.file_buf.split(|c| *c == b'\n').skip(2);
|
||||
|
||||
let mut lines = self.file_buf.split(|c| *c == b'\n');
|
||||
let Some(current_exercise_name) = lines.next() else {
|
||||
return StateFileStatus::NotRead;
|
||||
};
|
||||
|
@ -301,17 +300,13 @@ impl AppState {
|
|||
|
||||
// Write the state file.
|
||||
// The file's format is very simple:
|
||||
// - The first line is a comment.
|
||||
// - The second line is an empty line.
|
||||
// - The third line is the name of the current exercise. It must end with `\n` even if there
|
||||
// - The first line is the name of the current exercise. It must end with `\n` even if there
|
||||
// are no done exercises.
|
||||
// - The fourth line is an empty line.
|
||||
// - 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(b"DON'T EDIT THIS FILE!\n\n");
|
||||
self.file_buf
|
||||
.extend_from_slice(self.current_exercise().name.as_bytes());
|
||||
self.file_buf.push(b'\n');
|
||||
|
|
|
@ -1,57 +0,0 @@
|
|||
use anyhow::{Context, Result};
|
||||
|
||||
use crate::info_file::ExerciseInfo;
|
||||
|
||||
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<u8>,
|
||||
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");
|
||||
}
|
||||
}
|
||||
|
||||
pub fn updated_cargo_toml(
|
||||
exercise_infos: &[ExerciseInfo],
|
||||
current_cargo_toml: &str,
|
||||
exercise_path_prefix: &[u8],
|
||||
) -> Result<Vec<u8>> {
|
||||
let (bins_start_ind, bins_end_ind) = bins_start_end_ind(current_cargo_toml)?;
|
||||
|
||||
let mut updated_cargo_toml = Vec::with_capacity(1 << 13);
|
||||
updated_cargo_toml.extend_from_slice(current_cargo_toml[..bins_start_ind].as_bytes());
|
||||
append_bins(
|
||||
&mut updated_cargo_toml,
|
||||
exercise_infos,
|
||||
exercise_path_prefix,
|
||||
);
|
||||
updated_cargo_toml.extend_from_slice(current_cargo_toml[bins_end_ind..].as_bytes());
|
||||
|
||||
Ok(updated_cargo_toml)
|
||||
}
|
25
src/dev.rs
25
src/dev.rs
|
@ -1,39 +1,28 @@
|
|||
use std::path::PathBuf;
|
||||
|
||||
use anyhow::{bail, Context, Result};
|
||||
use clap::Subcommand;
|
||||
|
||||
use crate::DEBUG_PROFILE;
|
||||
use crate::DEVELOPING_OFFICIAL_RUSTLINGS;
|
||||
|
||||
mod check;
|
||||
mod new;
|
||||
mod init;
|
||||
mod update;
|
||||
|
||||
#[derive(Subcommand)]
|
||||
pub enum DevCommands {
|
||||
/// Create a new project for third-party Rustlings exercises
|
||||
New {
|
||||
/// The path to create the project in
|
||||
path: PathBuf,
|
||||
/// Don't initialize a Git repository in the project directory
|
||||
#[arg(long)]
|
||||
no_git: bool,
|
||||
},
|
||||
/// Run checks on the exercises
|
||||
Init,
|
||||
Check,
|
||||
/// Update the `Cargo.toml` file for the exercises
|
||||
Update,
|
||||
}
|
||||
|
||||
impl DevCommands {
|
||||
pub fn run(self) -> Result<()> {
|
||||
match self {
|
||||
DevCommands::New { path, no_git } => {
|
||||
if DEBUG_PROFILE {
|
||||
bail!("Disabled in the debug build");
|
||||
DevCommands::Init => {
|
||||
if DEVELOPING_OFFICIAL_RUSTLINGS {
|
||||
bail!("Disabled while developing the official Rustlings");
|
||||
}
|
||||
|
||||
new::new(&path, no_git).context(INIT_ERR)
|
||||
init::init().context(INIT_ERR)
|
||||
}
|
||||
DevCommands::Check => check::check(),
|
||||
DevCommands::Update => update::update(),
|
||||
|
|
|
@ -7,9 +7,8 @@ use std::{
|
|||
};
|
||||
|
||||
use crate::{
|
||||
cargo_toml::{append_bins, bins_start_end_ind},
|
||||
info_file::{ExerciseInfo, InfoFile},
|
||||
CURRENT_FORMAT_VERSION, DEBUG_PROFILE,
|
||||
CURRENT_FORMAT_VERSION, DEVELOPING_OFFICIAL_RUSTLINGS,
|
||||
};
|
||||
|
||||
fn forbidden_char(input: &str) -> Option<char> {
|
||||
|
@ -137,6 +136,41 @@ fn check_exercises(info_file: &InfoFile) -> Result<()> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
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<u8>,
|
||||
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,
|
||||
|
@ -149,13 +183,7 @@ fn check_cargo_toml(
|
|||
append_bins(&mut new_bins, exercise_infos, exercise_path_prefix);
|
||||
|
||||
if old_bins != new_bins {
|
||||
if DEBUG_PROFILE {
|
||||
bail!("The file `dev/Cargo.toml` is outdated. Please run `cargo run -- dev update` to update it");
|
||||
} else {
|
||||
bail!(
|
||||
"The file `Cargo.toml` is outdated. Please run `rustlings dev update` to update it",
|
||||
);
|
||||
}
|
||||
bail!("`Cargo.toml` is outdated. Run `rustlings dev update` to update it");
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
@ -165,16 +193,19 @@ pub fn check() -> Result<()> {
|
|||
let info_file = InfoFile::parse()?;
|
||||
check_exercises(&info_file)?;
|
||||
|
||||
if DEBUG_PROFILE {
|
||||
if DEVELOPING_OFFICIAL_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"")?;
|
||||
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!");
|
||||
|
|
|
@ -1,73 +1,41 @@
|
|||
use anyhow::{bail, Context, Result};
|
||||
use std::{
|
||||
env::set_current_dir,
|
||||
fs::{self, create_dir},
|
||||
path::Path,
|
||||
process::Command,
|
||||
};
|
||||
use anyhow::{Context, Result};
|
||||
use std::fs::{self, create_dir};
|
||||
|
||||
use crate::CURRENT_FORMAT_VERSION;
|
||||
|
||||
fn create_rel_dir(dir_name: &str, current_dir: &str) -> Result<()> {
|
||||
create_dir(dir_name)
|
||||
.with_context(|| format!("Failed to create the directory {current_dir}/{dir_name}"))?;
|
||||
println!("Created the directory {current_dir}/{dir_name}");
|
||||
Ok(())
|
||||
}
|
||||
pub fn init() -> Result<()> {
|
||||
create_dir("rustlings").context("Failed to create the directory `rustlings`")?;
|
||||
|
||||
fn write_rel_file<C>(file_name: &str, current_dir: &str, content: C) -> Result<()>
|
||||
where
|
||||
C: AsRef<[u8]>,
|
||||
{
|
||||
fs::write(file_name, content)
|
||||
.with_context(|| format!("Failed to create the file {current_dir}/{file_name}"))?;
|
||||
// Space to align with `create_rel_dir`.
|
||||
println!("Created the file {current_dir}/{file_name}");
|
||||
Ok(())
|
||||
}
|
||||
create_dir("rustlings/exercises")
|
||||
.context("Failed to create the directory `rustlings/exercises`")?;
|
||||
|
||||
pub fn new(path: &Path, no_git: bool) -> Result<()> {
|
||||
let dir_name = path.to_string_lossy();
|
||||
create_dir("rustlings/solutions")
|
||||
.context("Failed to create the directory `rustlings/solutions`")?;
|
||||
|
||||
create_dir(path).with_context(|| format!("Failed to create the directory {dir_name}"))?;
|
||||
println!("Created the directory {dir_name}");
|
||||
|
||||
set_current_dir(path)
|
||||
.with_context(|| format!("Failed to set {dir_name} as the current directory"))?;
|
||||
|
||||
if !no_git
|
||||
&& !Command::new("git")
|
||||
.arg("init")
|
||||
.status()
|
||||
.context("Failed to run `git init`")?
|
||||
.success()
|
||||
{
|
||||
bail!("`git init` didn't run successfully. See the error message above");
|
||||
}
|
||||
|
||||
write_rel_file(".gitignore", &dir_name, crate::init::GITIGNORE)?;
|
||||
|
||||
create_rel_dir("exercises", &dir_name)?;
|
||||
create_rel_dir("solutions", &dir_name)?;
|
||||
|
||||
write_rel_file(
|
||||
"info.toml",
|
||||
&dir_name,
|
||||
fs::write(
|
||||
"rustlings/info.toml",
|
||||
format!("{INFO_FILE_BEFORE_FORMAT_VERSION}{CURRENT_FORMAT_VERSION}{INFO_FILE_AFTER_FORMAT_VERSION}"),
|
||||
)?;
|
||||
)
|
||||
.context("Failed to create the file `rustlings/info.toml`")?;
|
||||
|
||||
write_rel_file("Cargo.toml", &dir_name, CARGO_TOML)?;
|
||||
fs::write("rustlings/Cargo.toml", CARGO_TOML)
|
||||
.context("Failed to create the file `rustlings/Cargo.toml`")?;
|
||||
|
||||
write_rel_file("README.md", &dir_name, README)?;
|
||||
fs::write("rustlings/.gitignore", crate::init::GITIGNORE)
|
||||
.context("Failed to create the file `rustlings/.gitignore`")?;
|
||||
|
||||
create_rel_dir(".vscode", &dir_name)?;
|
||||
write_rel_file(
|
||||
".vscode/extensions.json",
|
||||
&dir_name,
|
||||
fs::write("rustlings/README.md", README)
|
||||
.context("Failed to create the file `rustlings/README.md`")?;
|
||||
|
||||
create_dir("rustlings/.vscode")
|
||||
.context("Failed to create the directory `rustlings/.vscode`")?;
|
||||
fs::write(
|
||||
"rustlings/.vscode/extensions.json",
|
||||
crate::init::VS_CODE_EXTENSIONS_JSON,
|
||||
)?;
|
||||
)
|
||||
.context("Failed to create the file `rustlings/.vscode/extensions.json`")?;
|
||||
|
||||
println!("\nInitialization done ✓");
|
||||
println!("{INIT_DONE}");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -129,3 +97,13 @@ First,
|
|||
|
||||
Then, open your terminal in this directory and run `rustlings` to get started with the exercises 🚀
|
||||
";
|
||||
|
||||
const INIT_DONE: &str = r#"Initialization done!
|
||||
You can start developing third-party Rustlings exercises in the `rustlings` directory :D
|
||||
|
||||
If the initialization was done in a Rust project which is a Cargo workspace, you need to add the
|
||||
path to the `rustlings` directory to the `workspace.exclude` list in the project's `Cargo.toml`
|
||||
file. For example:
|
||||
|
||||
[workspace]
|
||||
exclude = ["rustlings"]"#;
|
|
@ -3,22 +3,26 @@ use std::fs;
|
|||
use anyhow::{Context, Result};
|
||||
|
||||
use crate::{
|
||||
cargo_toml::updated_cargo_toml,
|
||||
info_file::{ExerciseInfo, InfoFile},
|
||||
DEBUG_PROFILE,
|
||||
DEVELOPING_OFFICIAL_RUSTLINGS,
|
||||
};
|
||||
|
||||
use super::check::{append_bins, bins_start_end_ind};
|
||||
|
||||
fn update_cargo_toml(
|
||||
exercise_infos: &[ExerciseInfo],
|
||||
current_cargo_toml: &str,
|
||||
exercise_path_prefix: &[u8],
|
||||
cargo_toml_path: &str,
|
||||
exercise_path_prefix: &[u8],
|
||||
) -> Result<()> {
|
||||
let updated_cargo_toml =
|
||||
updated_cargo_toml(exercise_infos, current_cargo_toml, exercise_path_prefix)?;
|
||||
let (bins_start_ind, bins_end_ind) = bins_start_end_ind(current_cargo_toml)?;
|
||||
|
||||
fs::write(cargo_toml_path, updated_cargo_toml)
|
||||
.context("Failed to write the `Cargo.toml` file")?;
|
||||
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(())
|
||||
}
|
||||
|
@ -26,12 +30,12 @@ fn update_cargo_toml(
|
|||
pub fn update() -> Result<()> {
|
||||
let info_file = InfoFile::parse()?;
|
||||
|
||||
if DEBUG_PROFILE {
|
||||
if DEVELOPING_OFFICIAL_RUSTLINGS {
|
||||
update_cargo_toml(
|
||||
&info_file.exercises,
|
||||
include_str!("../../dev/Cargo.toml"),
|
||||
b"../",
|
||||
"dev/Cargo.toml",
|
||||
b"../",
|
||||
)
|
||||
.context("Failed to update the file `dev/Cargo.toml`")?;
|
||||
|
||||
|
@ -39,7 +43,7 @@ pub fn update() -> Result<()> {
|
|||
} 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, b"", "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`");
|
||||
|
|
|
@ -7,7 +7,7 @@ use std::{
|
|||
process::{Command, Output},
|
||||
};
|
||||
|
||||
use crate::{info_file::Mode, DEBUG_PROFILE};
|
||||
use crate::{info_file::Mode, DEVELOPING_OFFICIAL_RUSTLINGS};
|
||||
|
||||
pub struct TerminalFileLink<'a> {
|
||||
path: &'a str,
|
||||
|
@ -48,7 +48,7 @@ impl Exercise {
|
|||
cmd.arg(command);
|
||||
|
||||
// A hack to make `cargo run` work when developing Rustlings.
|
||||
if DEBUG_PROFILE && Path::new("tests").exists() {
|
||||
if DEVELOPING_OFFICIAL_RUSTLINGS && Path::new("tests").exists() {
|
||||
cmd.arg("--manifest-path").arg("dev/Cargo.toml");
|
||||
}
|
||||
|
||||
|
|
26
src/init.rs
26
src/init.rs
|
@ -6,7 +6,17 @@ use std::{
|
|||
path::Path,
|
||||
};
|
||||
|
||||
use crate::{cargo_toml::updated_cargo_toml, embedded::EMBEDDED_FILES, info_file::InfoFile};
|
||||
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
|
||||
};
|
||||
|
||||
pub fn init() -> Result<()> {
|
||||
if Path::new("exercises").is_dir() && Path::new("Cargo.toml").is_file() {
|
||||
|
@ -28,19 +38,7 @@ pub fn init() -> Result<()> {
|
|||
.init_exercises_dir()
|
||||
.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
|
||||
.as_bytes()
|
||||
.iter()
|
||||
.position(|c| *c == b'\n')
|
||||
.context("The embedded `Cargo.toml` is empty or contains only one line.")?;
|
||||
let current_cargo_toml =
|
||||
¤t_cargo_toml[(newline_ind + 1).min(current_cargo_toml.len() - 1)..];
|
||||
let updated_cargo_toml = updated_cargo_toml(&info_file.exercises, current_cargo_toml, b"")
|
||||
.context("Failed to generate `Cargo.toml`")?;
|
||||
fs::write("Cargo.toml", updated_cargo_toml)
|
||||
fs::write("Cargo.toml", CARGO_TOML)
|
||||
.context("Failed to create the file `rustlings/Cargo.toml`")?;
|
||||
|
||||
fs::write(".gitignore", GITIGNORE)
|
||||
|
|
29
src/main.rs
29
src/main.rs
|
@ -14,7 +14,6 @@ use std::{
|
|||
use self::{app_state::AppState, dev::DevCommands, info_file::InfoFile, watch::WatchExit};
|
||||
|
||||
mod app_state;
|
||||
mod cargo_toml;
|
||||
mod dev;
|
||||
mod embedded;
|
||||
mod exercise;
|
||||
|
@ -26,7 +25,7 @@ mod run;
|
|||
mod watch;
|
||||
|
||||
const CURRENT_FORMAT_VERSION: u8 = 1;
|
||||
const DEBUG_PROFILE: bool = {
|
||||
const DEVELOPING_OFFICIAL_RUSTLINGS: bool = {
|
||||
#[allow(unused_assignments, unused_mut)]
|
||||
let mut debug_profile = false;
|
||||
|
||||
|
@ -54,7 +53,7 @@ struct Args {
|
|||
enum Subcommands {
|
||||
/// Initialize Rustlings
|
||||
Init,
|
||||
/// Run a single exercise. Runs the next pending exercise if the exercise name is not specified
|
||||
/// Run a single exercise. Runs the next pending exercise if the exercise name is not specified.
|
||||
Run {
|
||||
/// The name of the exercise
|
||||
name: Option<String>,
|
||||
|
@ -64,12 +63,11 @@ enum Subcommands {
|
|||
/// The name of the exercise
|
||||
name: String,
|
||||
},
|
||||
/// Show a hint. Shows the hint of the next pending exercise if the exercise name is not specified
|
||||
/// Show a hint. Shows the hint of the next pending exercise if the exercise name is not specified.
|
||||
Hint {
|
||||
/// The name of the exercise
|
||||
name: Option<String>,
|
||||
},
|
||||
/// Commands for developing (third-party) Rustlings exercises
|
||||
#[command(subcommand)]
|
||||
Dev(DevCommands),
|
||||
}
|
||||
|
@ -77,24 +75,12 @@ enum Subcommands {
|
|||
fn main() -> Result<()> {
|
||||
let args = Args::parse();
|
||||
|
||||
if !DEBUG_PROFILE && Path::new("dev/rustlings-repo.txt").exists() {
|
||||
bail!("{OLD_METHOD_ERR}");
|
||||
}
|
||||
|
||||
which::which("cargo").context(CARGO_NOT_FOUND_ERR)?;
|
||||
|
||||
match args.command {
|
||||
Some(Subcommands::Init) => {
|
||||
if DEBUG_PROFILE {
|
||||
bail!("Disabled in the debug build");
|
||||
}
|
||||
|
||||
{
|
||||
let mut stdout = io::stdout().lock();
|
||||
stdout.write_all(b"This command will create the directory `rustlings/` which will contain the exercises.\nPress ENTER to continue ")?;
|
||||
stdout.flush()?;
|
||||
io::stdin().lock().read_until(b'\n', &mut Vec::new())?;
|
||||
stdout.write_all(b"\n")?;
|
||||
if DEVELOPING_OFFICIAL_RUSTLINGS {
|
||||
bail!("Disabled while developing the official Rustlings");
|
||||
}
|
||||
|
||||
return init::init().context("Initialization failed");
|
||||
|
@ -188,11 +174,6 @@ fn main() -> Result<()> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
const OLD_METHOD_ERR: &str = "You are trying to run Rustlings using the old method before v6.
|
||||
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`.
|
||||
Did you already install Rust?
|
||||
Try running `cargo --version` to diagnose the problem.";
|
||||
|
|
Loading…
Reference in a new issue