aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_project_model/src/lib.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_project_model/src/lib.rs')
-rw-r--r--crates/ra_project_model/src/lib.rs122
1 files changed, 49 insertions, 73 deletions
diff --git a/crates/ra_project_model/src/lib.rs b/crates/ra_project_model/src/lib.rs
index fe3e81689..8b85b4831 100644
--- a/crates/ra_project_model/src/lib.rs
+++ b/crates/ra_project_model/src/lib.rs
@@ -1,25 +1,25 @@
1//! FIXME: write short doc here 1//! FIXME: write short doc here
2 2
3mod cargo_workspace; 3mod cargo_workspace;
4mod json_project; 4mod project_json;
5mod sysroot; 5mod sysroot;
6 6
7use std::{ 7use std::{
8 fs::{read_dir, File, ReadDir}, 8 fs::{self, read_dir, ReadDir},
9 io::{self, BufReader}, 9 io,
10 path::{Path, PathBuf}, 10 path::Path,
11 process::{Command, Output}, 11 process::{Command, Output},
12}; 12};
13 13
14use anyhow::{bail, Context, Result}; 14use anyhow::{bail, Context, Result};
15use paths::{AbsPath, AbsPathBuf};
15use ra_cfg::CfgOptions; 16use ra_cfg::CfgOptions;
16use ra_db::{CrateGraph, CrateName, Edition, Env, FileId}; 17use ra_db::{CrateGraph, CrateId, CrateName, Edition, Env, FileId};
17use rustc_hash::{FxHashMap, FxHashSet}; 18use rustc_hash::{FxHashMap, FxHashSet};
18use serde_json::from_reader;
19 19
20pub use crate::{ 20pub use crate::{
21 cargo_workspace::{CargoConfig, CargoWorkspace, Package, Target, TargetKind}, 21 cargo_workspace::{CargoConfig, CargoWorkspace, Package, Target, TargetKind},
22 json_project::JsonProject, 22 project_json::{ProjectJson, ProjectJsonData},
23 sysroot::Sysroot, 23 sysroot::Sysroot,
24}; 24};
25pub use ra_proc_macro::ProcMacroClient; 25pub use ra_proc_macro::ProcMacroClient;
@@ -29,7 +29,7 @@ pub enum ProjectWorkspace {
29 /// Project workspace was discovered by running `cargo metadata` and `rustc --print sysroot`. 29 /// Project workspace was discovered by running `cargo metadata` and `rustc --print sysroot`.
30 Cargo { cargo: CargoWorkspace, sysroot: Sysroot }, 30 Cargo { cargo: CargoWorkspace, sysroot: Sysroot },
31 /// Project workspace was manually specified using a `rust-project.json` file. 31 /// Project workspace was manually specified using a `rust-project.json` file.
32 Json { project: JsonProject, project_location: PathBuf }, 32 Json { project: ProjectJson },
33} 33}
34 34
35/// `PackageRoot` describes a package root folder. 35/// `PackageRoot` describes a package root folder.
@@ -38,22 +38,22 @@ pub enum ProjectWorkspace {
38#[derive(Debug, Clone)] 38#[derive(Debug, Clone)]
39pub struct PackageRoot { 39pub struct PackageRoot {
40 /// Path to the root folder 40 /// Path to the root folder
41 path: PathBuf, 41 path: AbsPathBuf,
42 /// Is a member of the current workspace 42 /// Is a member of the current workspace
43 is_member: bool, 43 is_member: bool,
44 out_dir: Option<PathBuf>, 44 out_dir: Option<AbsPathBuf>,
45} 45}
46impl PackageRoot { 46impl PackageRoot {
47 pub fn new_member(path: PathBuf) -> PackageRoot { 47 pub fn new_member(path: AbsPathBuf) -> PackageRoot {
48 Self { path, is_member: true, out_dir: None } 48 Self { path, is_member: true, out_dir: None }
49 } 49 }
50 pub fn new_non_member(path: PathBuf) -> PackageRoot { 50 pub fn new_non_member(path: AbsPathBuf) -> PackageRoot {
51 Self { path, is_member: false, out_dir: None } 51 Self { path, is_member: false, out_dir: None }
52 } 52 }
53 pub fn path(&self) -> &Path { 53 pub fn path(&self) -> &AbsPath {
54 &self.path 54 &self.path
55 } 55 }
56 pub fn out_dir(&self) -> Option<&Path> { 56 pub fn out_dir(&self) -> Option<&AbsPath> {
57 self.out_dir.as_deref() 57 self.out_dir.as_deref()
58 } 58 }
59 pub fn is_member(&self) -> bool { 59 pub fn is_member(&self) -> bool {
@@ -63,12 +63,12 @@ impl PackageRoot {
63 63
64#[derive(Debug, Clone, PartialEq, Eq, Hash, Ord, PartialOrd)] 64#[derive(Debug, Clone, PartialEq, Eq, Hash, Ord, PartialOrd)]
65pub enum ProjectManifest { 65pub enum ProjectManifest {
66 ProjectJson(PathBuf), 66 ProjectJson(AbsPathBuf),
67 CargoToml(PathBuf), 67 CargoToml(AbsPathBuf),
68} 68}
69 69
70impl ProjectManifest { 70impl ProjectManifest {
71 pub fn from_manifest_file(path: PathBuf) -> Result<ProjectManifest> { 71 pub fn from_manifest_file(path: AbsPathBuf) -> Result<ProjectManifest> {
72 if path.ends_with("rust-project.json") { 72 if path.ends_with("rust-project.json") {
73 return Ok(ProjectManifest::ProjectJson(path)); 73 return Ok(ProjectManifest::ProjectJson(path));
74 } 74 }
@@ -78,7 +78,7 @@ impl ProjectManifest {
78 bail!("project root must point to Cargo.toml or rust-project.json: {}", path.display()) 78 bail!("project root must point to Cargo.toml or rust-project.json: {}", path.display())
79 } 79 }
80 80
81 pub fn discover_single(path: &Path) -> Result<ProjectManifest> { 81 pub fn discover_single(path: &AbsPath) -> Result<ProjectManifest> {
82 let mut candidates = ProjectManifest::discover(path)?; 82 let mut candidates = ProjectManifest::discover(path)?;
83 let res = match candidates.pop() { 83 let res = match candidates.pop() {
84 None => bail!("no projects"), 84 None => bail!("no projects"),
@@ -91,23 +91,23 @@ impl ProjectManifest {
91 Ok(res) 91 Ok(res)
92 } 92 }
93 93
94 pub fn discover(path: &Path) -> io::Result<Vec<ProjectManifest>> { 94 pub fn discover(path: &AbsPath) -> io::Result<Vec<ProjectManifest>> {
95 if let Some(project_json) = find_in_parent_dirs(path, "rust-project.json") { 95 if let Some(project_json) = find_in_parent_dirs(path, "rust-project.json") {
96 return Ok(vec![ProjectManifest::ProjectJson(project_json)]); 96 return Ok(vec![ProjectManifest::ProjectJson(project_json)]);
97 } 97 }
98 return find_cargo_toml(path) 98 return find_cargo_toml(path)
99 .map(|paths| paths.into_iter().map(ProjectManifest::CargoToml).collect()); 99 .map(|paths| paths.into_iter().map(ProjectManifest::CargoToml).collect());
100 100
101 fn find_cargo_toml(path: &Path) -> io::Result<Vec<PathBuf>> { 101 fn find_cargo_toml(path: &AbsPath) -> io::Result<Vec<AbsPathBuf>> {
102 match find_in_parent_dirs(path, "Cargo.toml") { 102 match find_in_parent_dirs(path, "Cargo.toml") {
103 Some(it) => Ok(vec![it]), 103 Some(it) => Ok(vec![it]),
104 None => Ok(find_cargo_toml_in_child_dir(read_dir(path)?)), 104 None => Ok(find_cargo_toml_in_child_dir(read_dir(path)?)),
105 } 105 }
106 } 106 }
107 107
108 fn find_in_parent_dirs(path: &Path, target_file_name: &str) -> Option<PathBuf> { 108 fn find_in_parent_dirs(path: &AbsPath, target_file_name: &str) -> Option<AbsPathBuf> {
109 if path.ends_with(target_file_name) { 109 if path.ends_with(target_file_name) {
110 return Some(path.to_owned()); 110 return Some(path.to_path_buf());
111 } 111 }
112 112
113 let mut curr = Some(path); 113 let mut curr = Some(path);
@@ -123,17 +123,18 @@ impl ProjectManifest {
123 None 123 None
124 } 124 }
125 125
126 fn find_cargo_toml_in_child_dir(entities: ReadDir) -> Vec<PathBuf> { 126 fn find_cargo_toml_in_child_dir(entities: ReadDir) -> Vec<AbsPathBuf> {
127 // Only one level down to avoid cycles the easy way and stop a runaway scan with large projects 127 // Only one level down to avoid cycles the easy way and stop a runaway scan with large projects
128 entities 128 entities
129 .filter_map(Result::ok) 129 .filter_map(Result::ok)
130 .map(|it| it.path().join("Cargo.toml")) 130 .map(|it| it.path().join("Cargo.toml"))
131 .filter(|it| it.exists()) 131 .filter(|it| it.exists())
132 .map(AbsPathBuf::assert)
132 .collect() 133 .collect()
133 } 134 }
134 } 135 }
135 136
136 pub fn discover_all(paths: &[impl AsRef<Path>]) -> Vec<ProjectManifest> { 137 pub fn discover_all(paths: &[impl AsRef<AbsPath>]) -> Vec<ProjectManifest> {
137 let mut res = paths 138 let mut res = paths
138 .iter() 139 .iter()
139 .filter_map(|it| ProjectManifest::discover(it.as_ref()).ok()) 140 .filter_map(|it| ProjectManifest::discover(it.as_ref()).ok())
@@ -154,20 +155,15 @@ impl ProjectWorkspace {
154 ) -> Result<ProjectWorkspace> { 155 ) -> Result<ProjectWorkspace> {
155 let res = match manifest { 156 let res = match manifest {
156 ProjectManifest::ProjectJson(project_json) => { 157 ProjectManifest::ProjectJson(project_json) => {
157 let file = File::open(&project_json).with_context(|| { 158 let file = fs::read_to_string(&project_json).with_context(|| {
158 format!("Failed to open json file {}", project_json.display()) 159 format!("Failed to read json file {}", project_json.display())
159 })?; 160 })?;
160 let reader = BufReader::new(file); 161 let data = serde_json::from_str(&file).with_context(|| {
161 let project_location = match project_json.parent() { 162 format!("Failed to deserialize json file {}", project_json.display())
162 Some(parent) => PathBuf::from(parent), 163 })?;
163 None => PathBuf::new(), 164 let project_location = project_json.parent().unwrap().to_path_buf();
164 }; 165 let project = ProjectJson::new(&project_location, data);
165 ProjectWorkspace::Json { 166 ProjectWorkspace::Json { project }
166 project: from_reader(reader).with_context(|| {
167 format!("Failed to deserialize json file {}", project_json.display())
168 })?,
169 project_location: project_location,
170 }
171 } 167 }
172 ProjectManifest::CargoToml(cargo_toml) => { 168 ProjectManifest::CargoToml(cargo_toml) => {
173 let cargo = CargoWorkspace::from_cargo_metadata(&cargo_toml, cargo_features) 169 let cargo = CargoWorkspace::from_cargo_metadata(&cargo_toml, cargo_features)
@@ -199,11 +195,9 @@ impl ProjectWorkspace {
199 /// the root is a member of the current workspace 195 /// the root is a member of the current workspace
200 pub fn to_roots(&self) -> Vec<PackageRoot> { 196 pub fn to_roots(&self) -> Vec<PackageRoot> {
201 match self { 197 match self {
202 ProjectWorkspace::Json { project, project_location } => project 198 ProjectWorkspace::Json { project } => {
203 .roots 199 project.roots.iter().map(|r| PackageRoot::new_member(r.path.clone())).collect()
204 .iter() 200 }
205 .map(|r| PackageRoot::new_member(project_location.join(&r.path)))
206 .collect(),
207 ProjectWorkspace::Cargo { cargo, sysroot } => cargo 201 ProjectWorkspace::Cargo { cargo, sysroot } => cargo
208 .packages() 202 .packages()
209 .map(|pkg| PackageRoot { 203 .map(|pkg| PackageRoot {
@@ -218,9 +212,9 @@ impl ProjectWorkspace {
218 } 212 }
219 } 213 }
220 214
221 pub fn proc_macro_dylib_paths(&self) -> Vec<PathBuf> { 215 pub fn proc_macro_dylib_paths(&self) -> Vec<AbsPathBuf> {
222 match self { 216 match self {
223 ProjectWorkspace::Json { project, .. } => project 217 ProjectWorkspace::Json { project } => project
224 .crates 218 .crates
225 .iter() 219 .iter()
226 .filter_map(|krate| krate.proc_macro_dylib_path.as_ref()) 220 .filter_map(|krate| krate.proc_macro_dylib_path.as_ref())
@@ -247,36 +241,18 @@ impl ProjectWorkspace {
247 &self, 241 &self,
248 target: Option<&str>, 242 target: Option<&str>,
249 proc_macro_client: &ProcMacroClient, 243 proc_macro_client: &ProcMacroClient,
250 load: &mut dyn FnMut(&Path) -> Option<FileId>, 244 load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
251 ) -> CrateGraph { 245 ) -> CrateGraph {
252 let mut crate_graph = CrateGraph::default(); 246 let mut crate_graph = CrateGraph::default();
253 match self { 247 match self {
254 ProjectWorkspace::Json { project, project_location } => { 248 ProjectWorkspace::Json { project } => {
255 let crates: FxHashMap<_, _> = project 249 let crates: FxHashMap<_, _> = project
256 .crates 250 .crates
257 .iter() 251 .iter()
258 .enumerate() 252 .enumerate()
259 .filter_map(|(seq_index, krate)| { 253 .filter_map(|(seq_index, krate)| {
260 let file_path = project_location.join(&krate.root_module); 254 let file_path = &krate.root_module;
261 let file_id = load(&file_path)?; 255 let file_id = load(&file_path)?;
262 let edition = match krate.edition {
263 json_project::Edition::Edition2015 => Edition::Edition2015,
264 json_project::Edition::Edition2018 => Edition::Edition2018,
265 };
266 let cfg_options = {
267 let mut opts = CfgOptions::default();
268 for cfg in &krate.cfg {
269 match cfg.find('=') {
270 None => opts.insert_atom(cfg.into()),
271 Some(pos) => {
272 let key = &cfg[..pos];
273 let value = cfg[pos + 1..].trim_matches('"');
274 opts.insert_key_value(key.into(), value.into());
275 }
276 }
277 }
278 opts
279 };
280 256
281 let mut env = Env::default(); 257 let mut env = Env::default();
282 if let Some(out_dir) = &krate.out_dir { 258 if let Some(out_dir) = &krate.out_dir {
@@ -291,13 +267,13 @@ impl ProjectWorkspace {
291 .map(|it| proc_macro_client.by_dylib_path(&it)); 267 .map(|it| proc_macro_client.by_dylib_path(&it));
292 // FIXME: No crate name in json definition such that we cannot add OUT_DIR to env 268 // FIXME: No crate name in json definition such that we cannot add OUT_DIR to env
293 Some(( 269 Some((
294 json_project::CrateId(seq_index), 270 CrateId(seq_index as u32),
295 crate_graph.add_crate_root( 271 crate_graph.add_crate_root(
296 file_id, 272 file_id,
297 edition, 273 krate.edition,
298 // FIXME json definitions can store the crate name 274 // FIXME json definitions can store the crate name
299 None, 275 None,
300 cfg_options, 276 krate.cfg.clone(),
301 env, 277 env,
302 proc_macro.unwrap_or_default(), 278 proc_macro.unwrap_or_default(),
303 ), 279 ),
@@ -307,8 +283,8 @@ impl ProjectWorkspace {
307 283
308 for (id, krate) in project.crates.iter().enumerate() { 284 for (id, krate) in project.crates.iter().enumerate() {
309 for dep in &krate.deps { 285 for dep in &krate.deps {
310 let from_crate_id = json_project::CrateId(id); 286 let from_crate_id = CrateId(id as u32);
311 let to_crate_id = dep.krate; 287 let to_crate_id = dep.crate_id;
312 if let (Some(&from), Some(&to)) = 288 if let (Some(&from), Some(&to)) =
313 (crates.get(&from_crate_id), crates.get(&to_crate_id)) 289 (crates.get(&from_crate_id), crates.get(&to_crate_id))
314 { 290 {
@@ -524,15 +500,15 @@ impl ProjectWorkspace {
524 crate_graph 500 crate_graph
525 } 501 }
526 502
527 pub fn workspace_root_for(&self, path: &Path) -> Option<&Path> { 503 pub fn workspace_root_for(&self, path: &Path) -> Option<&AbsPath> {
528 match self { 504 match self {
529 ProjectWorkspace::Cargo { cargo, .. } => { 505 ProjectWorkspace::Cargo { cargo, .. } => {
530 Some(cargo.workspace_root()).filter(|root| path.starts_with(root)) 506 Some(cargo.workspace_root()).filter(|root| path.starts_with(root))
531 } 507 }
532 ProjectWorkspace::Json { project: JsonProject { roots, .. }, .. } => roots 508 ProjectWorkspace::Json { project: ProjectJson { roots, .. }, .. } => roots
533 .iter() 509 .iter()
534 .find(|root| path.starts_with(&root.path)) 510 .find(|root| path.starts_with(&root.path))
535 .map(|root| root.path.as_ref()), 511 .map(|root| root.path.as_path()),
536 } 512 }
537 } 513 }
538} 514}