Skip to content

Commit 140c4e4

Browse files
committed
Improve initialization in a Cargo workspace
1 parent 337460d commit 140c4e4

File tree

1 file changed

+55
-22
lines changed

1 file changed

+55
-22
lines changed

src/init.rs

Lines changed: 55 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use anyhow::{bail, Context, Result};
22
use ratatui::crossterm::style::Stylize;
3+
use serde::Deserialize;
34
use std::{
45
env::set_current_dir,
56
fs::{self, create_dir},
@@ -13,36 +14,68 @@ use crate::{
1314
term::press_enter_prompt,
1415
};
1516

17+
#[derive(Deserialize)]
18+
struct CargoLocateProject {
19+
root: PathBuf,
20+
}
21+
1622
pub fn init() -> Result<()> {
1723
let rustlings_dir = Path::new("rustlings");
1824
if rustlings_dir.exists() {
1925
bail!(RUSTLINGS_DIR_ALREADY_EXISTS_ERR);
2026
}
2127

28+
let locate_project_output = Command::new("cargo")
29+
.arg("locate-project")
30+
.arg("-q")
31+
.arg("--workspace")
32+
.stdin(Stdio::null())
33+
.stderr(Stdio::inherit())
34+
.output()
35+
.context(CARGO_LOCATE_PROJECT_ERR)?;
36+
2237
let mut stdout = io::stdout().lock();
2338
let mut init_git = true;
2439

25-
let manifest_path = Command::new("cargo")
26-
.args(["locate-project", "--message-format=plain"])
27-
.output()?;
28-
if manifest_path.status.success() {
29-
let manifest_path: PathBuf = String::from_utf8_lossy(&manifest_path.stdout).trim().into();
30-
40+
if locate_project_output.status.success() {
3141
if Path::new("exercises").exists() && Path::new("solutions").exists() {
3242
bail!(IN_INITIALIZED_DIR_ERR);
3343
}
34-
if fs::read_to_string(manifest_path)?.contains("[workspace]") {
35-
// make sure "rustlings" is added to `workspace.members` by making
36-
// cargo initialize a new project
37-
let output = Command::new("cargo").args(["new", "rustlings"]).output()?;
38-
if !output.status.success() {
39-
bail!("Failed to initilize new workspace member");
40-
}
41-
fs::remove_dir_all("rustlings")?;
42-
init_git = false;
43-
} else {
44-
bail!(IN_NON_WORKSPACE_CARGO_PROJECT_ERR);
44+
45+
let workspace_manifest =
46+
serde_json::de::from_slice::<CargoLocateProject>(&locate_project_output.stdout)
47+
.context(
48+
"Failed to read the field `root` from the output of `cargo locate-project …`",
49+
)?
50+
.root;
51+
52+
let workspace_manifest_content = fs::read_to_string(&workspace_manifest)
53+
.with_context(|| format!("Failed to read the file {}", workspace_manifest.display()))?;
54+
if !workspace_manifest_content.contains("[workspace]\n")
55+
&& !workspace_manifest_content.contains("workspace.")
56+
{
57+
bail!("The current directory is already part of a Cargo project.\nPlease initialize Rustlings in a different directory");
4558
}
59+
60+
// Make sure "rustlings" is added to `workspace.members` by making
61+
// Cargo initialize a new project.
62+
let status = Command::new("cargo")
63+
.arg("new")
64+
.arg("-q")
65+
.arg("--vcs")
66+
.arg("none")
67+
.arg("rustlings")
68+
.stdin(Stdio::null())
69+
.stdout(Stdio::null())
70+
.status()?;
71+
if !status.success() {
72+
bail!("Failed to initilize a new Cargo workspace member.\nPlease initialize Rustlings in a different directory");
73+
}
74+
75+
stdout.write_all(b"The directory `rustlings` has been added to `workspace.members` in the Cargo.toml file of this Cargo workspace.\n")?;
76+
fs::remove_dir_all("rustlings")
77+
.context("Failed to remove the temporary directory `rustlings/`")?;
78+
init_git = false;
4679
}
4780

4881
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<()> {
117150
Ok(())
118151
}
119152

153+
const CARGO_LOCATE_PROJECT_ERR: &str = "Failed to run the command `cargo locate-project …`
154+
Did you already install Rust?
155+
Try running `cargo --version` to diagnose the problem.";
156+
120157
const INIT_SOLUTION_FILE: &[u8] = b"fn main() {
121158
// DON'T EDIT THIS SOLUTION FILE!
122159
// It will be automatically filled after you finish the exercise.
@@ -133,17 +170,13 @@ pub const VS_CODE_EXTENSIONS_JSON: &[u8] = br#"{"recommendations":["rust-lang.ru
133170
const IN_INITIALIZED_DIR_ERR: &str = "It looks like Rustlings is already initialized in this directory.
134171
135172
If you already initialized Rustlings, run the command `rustlings` for instructions on getting started with the exercises.
136-
Otherwise, please run `rustlings init` again in another directory.";
173+
Otherwise, please run `rustlings init` again in a different directory.";
137174

138175
const RUSTLINGS_DIR_ALREADY_EXISTS_ERR: &str =
139176
"A directory with the name `rustlings` already exists in the current directory.
140177
You probably already initialized Rustlings.
141178
Run `cd rustlings`
142179
Then run `rustlings` again";
143180

144-
const IN_NON_WORKSPACE_CARGO_PROJECT_ERR: &str = "\
145-
The current directory is already part of a cargo project.
146-
Please initialize rustlings in a different directory.";
147-
148181
const POST_INIT_MSG: &str = "Run `cd rustlings` to go into the generated directory.
149182
Then run `rustlings` to get started.";

0 commit comments

Comments
 (0)