Improve initialization in workspace

- Detect if we are in a cargo project more reliably.
  (e.g. if `rustlings init` is run in the `src/` directory)

- Refuse to initialize rustlings in a non-workspace cargo project.

- Automatically populate the `workspace.members` field if `rustlings init` is
  run in a workspace.

  This may be considered risky, as there is no guarantee that's what the user
  wanted to do. However, it is consistent with the behavior of `cargo new`.
  Also, newcomers to Rust are unlikely to accidentally be in a cargo workspace,
  as they won't know how to create one in the first place.

  The use case for initialization in a workspace is when a workshop organizer
  wants to use rustlings alongside other exerices and provide a single
  repository with everything in one place.
This commit is contained in:
Remo Senekowitsch 2024-08-08 12:51:27 +02:00
parent 8df66f7991
commit dc086c6bf1

View file

@ -4,7 +4,7 @@ use std::{
env::set_current_dir, env::set_current_dir,
fs::{self, create_dir}, fs::{self, create_dir},
io::{self, Write}, io::{self, Write},
path::Path, path::{Path, PathBuf},
process::{Command, Stdio}, process::{Command, Stdio},
}; };
@ -22,14 +22,27 @@ pub fn init() -> Result<()> {
let mut stdout = io::stdout().lock(); let mut stdout = io::stdout().lock();
let mut init_git = true; let mut init_git = true;
if Path::new("Cargo.toml").exists() { 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 Path::new("exercises").exists() && Path::new("solutions").exists() { if Path::new("exercises").exists() && Path::new("solutions").exists() {
bail!(IN_INITIALIZED_DIR_ERR); bail!(IN_INITIALIZED_DIR_ERR);
} }
if fs::read_to_string(manifest_path)?.contains("[workspace]") {
stdout.write_all(CARGO_TOML_EXISTS_PROMPT_MSG)?; // make sure "rustlings" is added to `workspace.members` by making
press_enter_prompt(&mut stdout)?; // cargo initialize a new project
init_git = false; 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);
}
} }
stdout.write_all(b"This command will create the directory `rustlings/` which will contain the exercises.\nPress ENTER to continue ")?; stdout.write_all(b"This command will create the directory `rustlings/` which will contain the exercises.\nPress ENTER to continue ")?;
@ -128,19 +141,9 @@ You probably already initialized Rustlings.
Run `cd rustlings` Run `cd rustlings`
Then run `rustlings` again"; 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! const IN_NON_WORKSPACE_CARGO_PROJECT_ERR: &str = "\
The current directory is already part of a cargo project.
=> It is recommended to abort with CTRL+C and initialize Rustlings in another directory <= Please initialize rustlings in a different 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. const POST_INIT_MSG: &str = "Run `cd rustlings` to go into the generated directory.
Then run `rustlings` to get started."; Then run `rustlings` to get started.";