diff --git a/dev/Cargo.toml b/dev/Cargo.toml index e66973e0..eddf0164 100644 --- a/dev/Cargo.toml +++ b/dev/Cargo.toml @@ -1,4 +1,4 @@ -# Don't edit the `bin` list manually! It is updated by `cargo run -- dev update` +# Don't edit the `bin` list manually! It is updated by `cargo run -- dev update`. This comment line will be stripped in `rustlings init`. bin = [ { name = "intro1", path = "../exercises/00_intro/intro1.rs" }, { name = "intro2", path = "../exercises/00_intro/intro2.rs" }, diff --git a/src/cargo_toml.rs b/src/cargo_toml.rs new file mode 100644 index 00000000..2345a7ee --- /dev/null +++ b/src/cargo_toml.rs @@ -0,0 +1,57 @@ +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, + 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> { + 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) +} diff --git a/src/dev/check.rs b/src/dev/check.rs index cd115b71..9859c3e3 100644 --- a/src/dev/check.rs +++ b/src/dev/check.rs @@ -7,6 +7,7 @@ use std::{ }; use crate::{ + cargo_toml::{append_bins, bins_start_end_ind}, info_file::{ExerciseInfo, InfoFile}, CURRENT_FORMAT_VERSION, DEBUG_PROFILE, }; @@ -136,41 +137,6 @@ 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, - 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, @@ -183,7 +149,13 @@ fn check_cargo_toml( 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"); + 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", + ); + } } Ok(()) @@ -198,14 +170,11 @@ pub fn check() -> Result<()> { &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", - )?; + check_cargo_toml(&info_file.exercises, ¤t_cargo_toml, b"")?; } println!("\nEverything looks fine!"); diff --git a/src/dev/update.rs b/src/dev/update.rs index 1f032f79..d2f20aae 100644 --- a/src/dev/update.rs +++ b/src/dev/update.rs @@ -3,26 +3,22 @@ use std::fs; use anyhow::{Context, Result}; use crate::{ + cargo_toml::updated_cargo_toml, info_file::{ExerciseInfo, InfoFile}, DEBUG_PROFILE, }; -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], + cargo_toml_path: &str, ) -> Result<()> { - let (bins_start_ind, bins_end_ind) = bins_start_end_ind(current_cargo_toml)?; + let updated_cargo_toml = + updated_cargo_toml(exercise_infos, current_cargo_toml, exercise_path_prefix)?; - 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")?; + fs::write(cargo_toml_path, updated_cargo_toml) + .context("Failed to write the `Cargo.toml` file")?; Ok(()) } @@ -34,8 +30,8 @@ pub fn update() -> Result<()> { update_cargo_toml( &info_file.exercises, include_str!("../../dev/Cargo.toml"), - "dev/Cargo.toml", b"../", + "dev/Cargo.toml", ) .context("Failed to update the file `dev/Cargo.toml`")?; @@ -43,7 +39,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, "Cargo.toml", b"") + update_cargo_toml(&info_file.exercises, ¤t_cargo_toml, b"", "Cargo.toml") .context("Failed to update the file `Cargo.toml`")?; println!("Updated `Cargo.toml`"); diff --git a/src/init.rs b/src/init.rs index 52315e24..f210db77 100644 --- a/src/init.rs +++ b/src/init.rs @@ -6,17 +6,7 @@ use std::{ path::Path, }; -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 -}; +use crate::{cargo_toml::updated_cargo_toml, embedded::EMBEDDED_FILES, info_file::InfoFile}; pub fn init() -> Result<()> { if Path::new("exercises").is_dir() && Path::new("Cargo.toml").is_file() { @@ -38,7 +28,19 @@ pub fn init() -> Result<()> { .init_exercises_dir() .context("Failed to initialize the `rustlings/exercises` directory")?; - fs::write("Cargo.toml", CARGO_TOML) + 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) .context("Failed to create the file `rustlings/Cargo.toml`")?; fs::write(".gitignore", GITIGNORE) diff --git a/src/main.rs b/src/main.rs index bf2498a9..494c40d4 100644 --- a/src/main.rs +++ b/src/main.rs @@ -14,6 +14,7 @@ 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;