From 140c4e4812eff982cc3e0c9df6fa5076d7b56633 Mon Sep 17 00:00:00 2001 From: mo8it Date: Fri, 9 Aug 2024 00:49:30 +0200 Subject: [PATCH] Improve initialization in a Cargo workspace --- src/init.rs | 77 ++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 55 insertions(+), 22 deletions(-) diff --git a/src/init.rs b/src/init.rs index 94551c4c..3a0e11d2 100644 --- a/src/init.rs +++ b/src/init.rs @@ -1,5 +1,6 @@ use anyhow::{bail, Context, Result}; use ratatui::crossterm::style::Stylize; +use serde::Deserialize; use std::{ env::set_current_dir, fs::{self, create_dir}, @@ -13,36 +14,68 @@ 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::inherit()) + .output() + .context(CARGO_LOCATE_PROJECT_ERR)?; + let mut stdout = io::stdout().lock(); let mut init_git = true; - let manifest_path = Command::new("cargo") - .args(["locate-project", "--message-format=plain"]) - .output()?; - if manifest_path.status.success() { - let manifest_path: PathBuf = String::from_utf8_lossy(&manifest_path.stdout).trim().into(); - + if locate_project_output.status.success() { if Path::new("exercises").exists() && Path::new("solutions").exists() { bail!(IN_INITIALIZED_DIR_ERR); } - if fs::read_to_string(manifest_path)?.contains("[workspace]") { - // make sure "rustlings" is added to `workspace.members` by making - // cargo initialize a new project - let output = Command::new("cargo").args(["new", "rustlings"]).output()?; - if !output.status.success() { - bail!("Failed to initilize new workspace member"); - } - fs::remove_dir_all("rustlings")?; - init_git = false; - } else { - bail!(IN_NON_WORKSPACE_CARGO_PROJECT_ERR); + + let workspace_manifest = + serde_json::de::from_slice::(&locate_project_output.stdout) + .context( + "Failed to read the field `root` from the output of `cargo locate-project …`", + )? + .root; + + 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"); } + + // 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 initilize 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; } stdout.write_all(b"This command will create the directory `rustlings/` which will contain the exercises.\nPress ENTER to continue ")?; @@ -117,6 +150,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. @@ -133,7 +170,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. @@ -141,9 +178,5 @@ You probably already initialized Rustlings. Run `cd rustlings` Then run `rustlings` again"; -const IN_NON_WORKSPACE_CARGO_PROJECT_ERR: &str = "\ -The current directory is already part of a cargo project. -Please initialize rustlings in a different directory."; - const POST_INIT_MSG: &str = "Run `cd rustlings` to go into the generated directory. Then run `rustlings` to get started.";