1
1
use anyhow:: { bail, Context , Result } ;
2
2
use ratatui:: crossterm:: style:: Stylize ;
3
+ use serde:: Deserialize ;
3
4
use std:: {
4
5
env:: set_current_dir,
5
6
fs:: { self , create_dir} ,
@@ -13,36 +14,68 @@ use crate::{
13
14
term:: press_enter_prompt,
14
15
} ;
15
16
17
+ #[ derive( Deserialize ) ]
18
+ struct CargoLocateProject {
19
+ root : PathBuf ,
20
+ }
21
+
16
22
pub fn init ( ) -> Result < ( ) > {
17
23
let rustlings_dir = Path :: new ( "rustlings" ) ;
18
24
if rustlings_dir. exists ( ) {
19
25
bail ! ( RUSTLINGS_DIR_ALREADY_EXISTS_ERR ) ;
20
26
}
21
27
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
+
22
37
let mut stdout = io:: stdout ( ) . lock ( ) ;
23
38
let mut init_git = true ;
24
39
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 ( ) {
31
41
if Path :: new ( "exercises" ) . exists ( ) && Path :: new ( "solutions" ) . exists ( ) {
32
42
bail ! ( IN_INITIALIZED_DIR_ERR ) ;
33
43
}
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.\n Please initialize Rustlings in a different directory" ) ;
45
58
}
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.\n Please 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 ;
46
79
}
47
80
48
81
stdout. write_all ( b"This command will create the directory `rustlings/` which will contain the exercises.\n Press ENTER to continue " ) ?;
@@ -117,6 +150,10 @@ pub fn init() -> Result<()> {
117
150
Ok ( ( ) )
118
151
}
119
152
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
+
120
157
const INIT_SOLUTION_FILE : & [ u8 ] = b"fn main() {
121
158
// DON'T EDIT THIS SOLUTION FILE!
122
159
// 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
133
170
const IN_INITIALIZED_DIR_ERR : & str = "It looks like Rustlings is already initialized in this directory.
134
171
135
172
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." ;
137
174
138
175
const RUSTLINGS_DIR_ALREADY_EXISTS_ERR : & str =
139
176
"A directory with the name `rustlings` already exists in the current directory.
140
177
You probably already initialized Rustlings.
141
178
Run `cd rustlings`
142
179
Then run `rustlings` again" ;
143
180
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
-
148
181
const POST_INIT_MSG : & str = "Run `cd rustlings` to go into the generated directory.
149
182
Then run `rustlings` to get started." ;
0 commit comments