@@ -5,9 +5,8 @@ mod json_project;
5
5
mod sysroot;
6
6
7
7
use std:: {
8
- error:: Error ,
9
8
fs:: { read_dir, File , ReadDir } ,
10
- io:: BufReader ,
9
+ io:: { self , BufReader } ,
11
10
path:: { Path , PathBuf } ,
12
11
process:: Command ,
13
12
} ;
@@ -25,25 +24,6 @@ pub use crate::{
25
24
} ;
26
25
pub use ra_proc_macro:: ProcMacroClient ;
27
26
28
- #[ derive( Clone , PartialEq , Eq , Hash , Debug ) ]
29
- pub struct CargoTomlNotFoundError {
30
- pub searched_at : PathBuf ,
31
- pub reason : String ,
32
- }
33
-
34
- impl std:: fmt:: Display for CargoTomlNotFoundError {
35
- fn fmt ( & self , fmt : & mut std:: fmt:: Formatter < ' _ > ) -> std:: fmt:: Result {
36
- write ! (
37
- fmt,
38
- "can't find Cargo.toml at {}, due to {}" ,
39
- self . searched_at. display( ) ,
40
- self . reason
41
- )
42
- }
43
- }
44
-
45
- impl Error for CargoTomlNotFoundError { }
46
-
47
27
#[ derive( Debug , Clone ) ]
48
28
pub enum ProjectWorkspace {
49
29
/// Project workspace was discovered by running `cargo metadata` and `rustc --print sysroot`.
@@ -77,31 +57,119 @@ impl PackageRoot {
77
57
}
78
58
}
79
59
80
- impl ProjectWorkspace {
81
- pub fn discover ( path : & Path , cargo_features : & CargoConfig ) -> Result < ProjectWorkspace > {
82
- ProjectWorkspace :: discover_with_sysroot ( path, true , cargo_features)
60
+ #[ derive( Debug , Clone , PartialEq , Eq , Hash ) ]
61
+ pub enum ProjectRoot {
62
+ ProjectJson ( PathBuf ) ,
63
+ CargoToml ( PathBuf ) ,
64
+ }
65
+
66
+ impl ProjectRoot {
67
+ pub fn from_manifest_file ( path : PathBuf ) -> Result < ProjectRoot > {
68
+ if path. ends_with ( "rust-project.json" ) {
69
+ return Ok ( ProjectRoot :: ProjectJson ( path) ) ;
70
+ }
71
+ if path. ends_with ( "Cargo.toml" ) {
72
+ return Ok ( ProjectRoot :: CargoToml ( path) ) ;
73
+ }
74
+ bail ! ( "project root must point to Cargo.toml or rust-project.json: {}" , path. display( ) )
83
75
}
84
76
85
- pub fn discover_with_sysroot (
86
- path : & Path ,
87
- with_sysroot : bool ,
77
+ pub fn discover_single ( path : & Path ) -> Result < ProjectRoot > {
78
+ let mut candidates = ProjectRoot :: discover ( path) ?;
79
+ let res = match candidates. pop ( ) {
80
+ None => bail ! ( "no projects" ) ,
81
+ Some ( it) => it,
82
+ } ;
83
+
84
+ if !candidates. is_empty ( ) {
85
+ bail ! ( "more than one project" )
86
+ }
87
+ Ok ( res)
88
+ }
89
+
90
+ pub fn discover ( path : & Path ) -> io:: Result < Vec < ProjectRoot > > {
91
+ if let Some ( project_json) = find_rust_project_json ( path) {
92
+ return Ok ( vec ! [ ProjectRoot :: ProjectJson ( project_json) ] ) ;
93
+ }
94
+ return find_cargo_toml ( path)
95
+ . map ( |paths| paths. into_iter ( ) . map ( ProjectRoot :: CargoToml ) . collect ( ) ) ;
96
+
97
+ fn find_rust_project_json ( path : & Path ) -> Option < PathBuf > {
98
+ if path. ends_with ( "rust-project.json" ) {
99
+ return Some ( path. to_path_buf ( ) ) ;
100
+ }
101
+
102
+ let mut curr = Some ( path) ;
103
+ while let Some ( path) = curr {
104
+ let candidate = path. join ( "rust-project.json" ) ;
105
+ if candidate. exists ( ) {
106
+ return Some ( candidate) ;
107
+ }
108
+ curr = path. parent ( ) ;
109
+ }
110
+
111
+ None
112
+ }
113
+
114
+ fn find_cargo_toml ( path : & Path ) -> io:: Result < Vec < PathBuf > > {
115
+ if path. ends_with ( "Cargo.toml" ) {
116
+ return Ok ( vec ! [ path. to_path_buf( ) ] ) ;
117
+ }
118
+
119
+ if let Some ( p) = find_cargo_toml_in_parent_dir ( path) {
120
+ return Ok ( vec ! [ p] ) ;
121
+ }
122
+
123
+ let entities = read_dir ( path) ?;
124
+ Ok ( find_cargo_toml_in_child_dir ( entities) )
125
+ }
126
+
127
+ fn find_cargo_toml_in_parent_dir ( path : & Path ) -> Option < PathBuf > {
128
+ let mut curr = Some ( path) ;
129
+ while let Some ( path) = curr {
130
+ let candidate = path. join ( "Cargo.toml" ) ;
131
+ if candidate. exists ( ) {
132
+ return Some ( candidate) ;
133
+ }
134
+ curr = path. parent ( ) ;
135
+ }
136
+
137
+ None
138
+ }
139
+
140
+ fn find_cargo_toml_in_child_dir ( entities : ReadDir ) -> Vec < PathBuf > {
141
+ // Only one level down to avoid cycles the easy way and stop a runaway scan with large projects
142
+ let mut valid_canditates = vec ! [ ] ;
143
+ for entity in entities. filter_map ( Result :: ok) {
144
+ let candidate = entity. path ( ) . join ( "Cargo.toml" ) ;
145
+ if candidate. exists ( ) {
146
+ valid_canditates. push ( candidate)
147
+ }
148
+ }
149
+ valid_canditates
150
+ }
151
+ }
152
+ }
153
+
154
+ impl ProjectWorkspace {
155
+ pub fn load (
156
+ root : ProjectRoot ,
88
157
cargo_features : & CargoConfig ,
158
+ with_sysroot : bool ,
89
159
) -> Result < ProjectWorkspace > {
90
- match find_rust_project_json ( path) {
91
- Some ( json_path) => {
92
- let file = File :: open ( & json_path)
93
- . with_context ( || format ! ( "Failed to open json file {}" , json_path. display( ) ) ) ?;
160
+ let res = match root {
161
+ ProjectRoot :: ProjectJson ( project_json) => {
162
+ let file = File :: open ( & project_json) . with_context ( || {
163
+ format ! ( "Failed to open json file {}" , project_json. display( ) )
164
+ } ) ?;
94
165
let reader = BufReader :: new ( file) ;
95
- Ok ( ProjectWorkspace :: Json {
166
+ ProjectWorkspace :: Json {
96
167
project : from_reader ( reader) . with_context ( || {
97
- format ! ( "Failed to deserialize json file {}" , json_path . display( ) )
168
+ format ! ( "Failed to deserialize json file {}" , project_json . display( ) )
98
169
} ) ?,
99
- } )
170
+ }
100
171
}
101
- None => {
102
- let cargo_toml = find_cargo_toml ( path) . with_context ( || {
103
- format ! ( "Failed to find Cargo.toml for path {}" , path. display( ) )
104
- } ) ?;
172
+ ProjectRoot :: CargoToml ( cargo_toml) => {
105
173
let cargo = CargoWorkspace :: from_cargo_metadata ( & cargo_toml, cargo_features)
106
174
. with_context ( || {
107
175
format ! (
@@ -119,9 +187,11 @@ impl ProjectWorkspace {
119
187
} else {
120
188
Sysroot :: default ( )
121
189
} ;
122
- Ok ( ProjectWorkspace :: Cargo { cargo, sysroot } )
190
+ ProjectWorkspace :: Cargo { cargo, sysroot }
123
191
}
124
- }
192
+ } ;
193
+
194
+ Ok ( res)
125
195
}
126
196
127
197
/// Returns the roots for the current `ProjectWorkspace`
@@ -469,87 +539,6 @@ impl ProjectWorkspace {
469
539
}
470
540
}
471
541
472
- fn find_rust_project_json ( path : & Path ) -> Option < PathBuf > {
473
- if path. ends_with ( "rust-project.json" ) {
474
- return Some ( path. to_path_buf ( ) ) ;
475
- }
476
-
477
- let mut curr = Some ( path) ;
478
- while let Some ( path) = curr {
479
- let candidate = path. join ( "rust-project.json" ) ;
480
- if candidate. exists ( ) {
481
- return Some ( candidate) ;
482
- }
483
- curr = path. parent ( ) ;
484
- }
485
-
486
- None
487
- }
488
-
489
- fn find_cargo_toml_in_parent_dir ( path : & Path ) -> Option < PathBuf > {
490
- let mut curr = Some ( path) ;
491
- while let Some ( path) = curr {
492
- let candidate = path. join ( "Cargo.toml" ) ;
493
- if candidate. exists ( ) {
494
- return Some ( candidate) ;
495
- }
496
- curr = path. parent ( ) ;
497
- }
498
-
499
- None
500
- }
501
-
502
- fn find_cargo_toml_in_child_dir ( entities : ReadDir ) -> Vec < PathBuf > {
503
- // Only one level down to avoid cycles the easy way and stop a runaway scan with large projects
504
- let mut valid_canditates = vec ! [ ] ;
505
- for entity in entities. filter_map ( Result :: ok) {
506
- let candidate = entity. path ( ) . join ( "Cargo.toml" ) ;
507
- if candidate. exists ( ) {
508
- valid_canditates. push ( candidate)
509
- }
510
- }
511
- valid_canditates
512
- }
513
-
514
- fn find_cargo_toml ( path : & Path ) -> Result < PathBuf > {
515
- if path. ends_with ( "Cargo.toml" ) {
516
- return Ok ( path. to_path_buf ( ) ) ;
517
- }
518
-
519
- if let Some ( p) = find_cargo_toml_in_parent_dir ( path) {
520
- return Ok ( p) ;
521
- }
522
-
523
- let entities = match read_dir ( path) {
524
- Ok ( entities) => entities,
525
- Err ( e) => {
526
- return Err ( CargoTomlNotFoundError {
527
- searched_at : path. to_path_buf ( ) ,
528
- reason : format ! ( "file system error: {}" , e) ,
529
- }
530
- . into ( ) ) ;
531
- }
532
- } ;
533
-
534
- let mut valid_canditates = find_cargo_toml_in_child_dir ( entities) ;
535
- match valid_canditates. len ( ) {
536
- 1 => Ok ( valid_canditates. remove ( 0 ) ) ,
537
- 0 => Err ( CargoTomlNotFoundError {
538
- searched_at : path. to_path_buf ( ) ,
539
- reason : "no Cargo.toml file found" . to_string ( ) ,
540
- }
541
- . into ( ) ) ,
542
- _ => Err ( CargoTomlNotFoundError {
543
- searched_at : path. to_path_buf ( ) ,
544
- reason : format ! (
545
- "multiple equally valid Cargo.toml files found: {:?}" ,
546
- valid_canditates
547
- ) ,
548
- }
549
- . into ( ) ) ,
550
- }
551
- }
552
-
553
542
pub fn get_rustc_cfg_options ( ) -> CfgOptions {
554
543
let mut cfg_options = CfgOptions :: default ( ) ;
555
544
0 commit comments