@@ -77,31 +77,131 @@ impl PackageRoot {
7777 }
7878}
7979
80- impl ProjectWorkspace {
81- pub fn discover ( path : & Path , cargo_features : & CargoConfig ) -> Result < ProjectWorkspace > {
82- ProjectWorkspace :: discover_with_sysroot ( path, true , cargo_features)
80+ #[ derive( Debug , Clone , PartialEq , Eq , Hash ) ]
81+ pub enum ProjectRoot {
82+ ProjectJson ( PathBuf ) ,
83+ CargoToml ( PathBuf ) ,
84+ }
85+
86+ impl ProjectRoot {
87+ pub fn from_manifest_file ( path : PathBuf ) -> Result < ProjectRoot > {
88+ if path. ends_with ( "rust-project.json" ) {
89+ return Ok ( ProjectRoot :: ProjectJson ( path) ) ;
90+ }
91+ if path. ends_with ( "Cargo.toml" ) {
92+ return Ok ( ProjectRoot :: CargoToml ( path) ) ;
93+ }
94+ bail ! ( "project root must point to Cargo.toml or rust-project.json: {}" , path. display( ) )
8395 }
8496
85- pub fn discover_with_sysroot (
86- path : & Path ,
87- with_sysroot : bool ,
97+ pub fn discover ( path : & Path ) -> Result < ProjectRoot , CargoTomlNotFoundError > {
98+ if let Some ( project_json) = find_rust_project_json ( path) {
99+ return Ok ( ProjectRoot :: ProjectJson ( project_json) ) ;
100+ }
101+ return find_cargo_toml ( path) . map ( ProjectRoot :: CargoToml ) ;
102+
103+ fn find_rust_project_json ( path : & Path ) -> Option < PathBuf > {
104+ if path. ends_with ( "rust-project.json" ) {
105+ return Some ( path. to_path_buf ( ) ) ;
106+ }
107+
108+ let mut curr = Some ( path) ;
109+ while let Some ( path) = curr {
110+ let candidate = path. join ( "rust-project.json" ) ;
111+ if candidate. exists ( ) {
112+ return Some ( candidate) ;
113+ }
114+ curr = path. parent ( ) ;
115+ }
116+
117+ None
118+ }
119+
120+ fn find_cargo_toml ( path : & Path ) -> Result < PathBuf , CargoTomlNotFoundError > {
121+ if path. ends_with ( "Cargo.toml" ) {
122+ return Ok ( path. to_path_buf ( ) ) ;
123+ }
124+
125+ if let Some ( p) = find_cargo_toml_in_parent_dir ( path) {
126+ return Ok ( p) ;
127+ }
128+
129+ let entities = match read_dir ( path) {
130+ Ok ( entities) => entities,
131+ Err ( e) => {
132+ return Err ( CargoTomlNotFoundError {
133+ searched_at : path. to_path_buf ( ) ,
134+ reason : format ! ( "file system error: {}" , e) ,
135+ }
136+ . into ( ) ) ;
137+ }
138+ } ;
139+
140+ let mut valid_canditates = find_cargo_toml_in_child_dir ( entities) ;
141+ return match valid_canditates. len ( ) {
142+ 1 => Ok ( valid_canditates. remove ( 0 ) ) ,
143+ 0 => Err ( CargoTomlNotFoundError {
144+ searched_at : path. to_path_buf ( ) ,
145+ reason : "no Cargo.toml file found" . to_string ( ) ,
146+ }
147+ . into ( ) ) ,
148+ _ => Err ( CargoTomlNotFoundError {
149+ searched_at : path. to_path_buf ( ) ,
150+ reason : format ! (
151+ "multiple equally valid Cargo.toml files found: {:?}" ,
152+ valid_canditates
153+ ) ,
154+ }
155+ . into ( ) ) ,
156+ } ;
157+ }
158+
159+ fn find_cargo_toml_in_parent_dir ( path : & Path ) -> Option < PathBuf > {
160+ let mut curr = Some ( path) ;
161+ while let Some ( path) = curr {
162+ let candidate = path. join ( "Cargo.toml" ) ;
163+ if candidate. exists ( ) {
164+ return Some ( candidate) ;
165+ }
166+ curr = path. parent ( ) ;
167+ }
168+
169+ None
170+ }
171+
172+ fn find_cargo_toml_in_child_dir ( entities : ReadDir ) -> Vec < PathBuf > {
173+ // Only one level down to avoid cycles the easy way and stop a runaway scan with large projects
174+ let mut valid_canditates = vec ! [ ] ;
175+ for entity in entities. filter_map ( Result :: ok) {
176+ let candidate = entity. path ( ) . join ( "Cargo.toml" ) ;
177+ if candidate. exists ( ) {
178+ valid_canditates. push ( candidate)
179+ }
180+ }
181+ valid_canditates
182+ }
183+ }
184+ }
185+
186+ impl ProjectWorkspace {
187+ pub fn load (
188+ root : ProjectRoot ,
88189 cargo_features : & CargoConfig ,
190+ with_sysroot : bool ,
89191 ) -> 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( ) ) ) ?;
192+ let res = match root {
193+ ProjectRoot :: ProjectJson ( project_json) => {
194+ let file = File :: open ( & project_json) . with_context ( || {
195+ format ! ( "Failed to open json file {}" , project_json. display( ) )
196+ } ) ?;
94197 let reader = BufReader :: new ( file) ;
95- Ok ( ProjectWorkspace :: Json {
198+ ProjectWorkspace :: Json {
96199 project : from_reader ( reader) . with_context ( || {
97- format ! ( "Failed to deserialize json file {}" , json_path . display( ) )
200+ format ! ( "Failed to deserialize json file {}" , project_json . display( ) )
98201 } ) ?,
99- } )
202+ }
100203 }
101- None => {
102- let cargo_toml = find_cargo_toml ( path) . with_context ( || {
103- format ! ( "Failed to find Cargo.toml for path {}" , path. display( ) )
104- } ) ?;
204+ ProjectRoot :: CargoToml ( cargo_toml) => {
105205 let cargo = CargoWorkspace :: from_cargo_metadata ( & cargo_toml, cargo_features)
106206 . with_context ( || {
107207 format ! (
@@ -119,9 +219,11 @@ impl ProjectWorkspace {
119219 } else {
120220 Sysroot :: default ( )
121221 } ;
122- Ok ( ProjectWorkspace :: Cargo { cargo, sysroot } )
222+ ProjectWorkspace :: Cargo { cargo, sysroot }
123223 }
124- }
224+ } ;
225+
226+ Ok ( res)
125227 }
126228
127229 /// Returns the roots for the current `ProjectWorkspace`
@@ -469,87 +571,6 @@ impl ProjectWorkspace {
469571 }
470572}
471573
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-
553574pub fn get_rustc_cfg_options ( ) -> CfgOptions {
554575 let mut cfg_options = CfgOptions :: default ( ) ;
555576
0 commit comments