diff options
Diffstat (limited to 'crates/ra_project_model/src')
-rw-r--r-- | crates/ra_project_model/src/cargo_workspace.rs | 2 | ||||
-rw-r--r-- | crates/ra_project_model/src/json_project.rs | 89 | ||||
-rw-r--r-- | crates/ra_project_model/src/lib.rs | 52 |
3 files changed, 122 insertions, 21 deletions
diff --git a/crates/ra_project_model/src/cargo_workspace.rs b/crates/ra_project_model/src/cargo_workspace.rs index a306ce95f..4b7444039 100644 --- a/crates/ra_project_model/src/cargo_workspace.rs +++ b/crates/ra_project_model/src/cargo_workspace.rs | |||
@@ -64,7 +64,7 @@ impl Default for CargoConfig { | |||
64 | fn default() -> Self { | 64 | fn default() -> Self { |
65 | CargoConfig { | 65 | CargoConfig { |
66 | no_default_features: false, | 66 | no_default_features: false, |
67 | all_features: true, | 67 | all_features: false, |
68 | features: Vec::new(), | 68 | features: Vec::new(), |
69 | load_out_dirs_from_check: false, | 69 | load_out_dirs_from_check: false, |
70 | target: None, | 70 | target: None, |
diff --git a/crates/ra_project_model/src/json_project.rs b/crates/ra_project_model/src/json_project.rs index b030c8a6a..09c06fef9 100644 --- a/crates/ra_project_model/src/json_project.rs +++ b/crates/ra_project_model/src/json_project.rs | |||
@@ -5,6 +5,13 @@ use std::path::PathBuf; | |||
5 | use rustc_hash::{FxHashMap, FxHashSet}; | 5 | use rustc_hash::{FxHashMap, FxHashSet}; |
6 | use serde::Deserialize; | 6 | use serde::Deserialize; |
7 | 7 | ||
8 | /// Roots and crates that compose this Rust project. | ||
9 | #[derive(Clone, Debug, Deserialize)] | ||
10 | pub struct JsonProject { | ||
11 | pub(crate) roots: Vec<Root>, | ||
12 | pub(crate) crates: Vec<Crate>, | ||
13 | } | ||
14 | |||
8 | /// A root points to the directory which contains Rust crates. rust-analyzer watches all files in | 15 | /// A root points to the directory which contains Rust crates. rust-analyzer watches all files in |
9 | /// all roots. Roots might be nested. | 16 | /// all roots. Roots might be nested. |
10 | #[derive(Clone, Debug, Deserialize)] | 17 | #[derive(Clone, Debug, Deserialize)] |
@@ -20,8 +27,17 @@ pub struct Crate { | |||
20 | pub(crate) root_module: PathBuf, | 27 | pub(crate) root_module: PathBuf, |
21 | pub(crate) edition: Edition, | 28 | pub(crate) edition: Edition, |
22 | pub(crate) deps: Vec<Dep>, | 29 | pub(crate) deps: Vec<Dep>, |
30 | |||
31 | // This is the preferred method of providing cfg options. | ||
32 | #[serde(default)] | ||
33 | pub(crate) cfg: FxHashSet<String>, | ||
34 | |||
35 | // These two are here for transition only. | ||
36 | #[serde(default)] | ||
23 | pub(crate) atom_cfgs: FxHashSet<String>, | 37 | pub(crate) atom_cfgs: FxHashSet<String>, |
38 | #[serde(default)] | ||
24 | pub(crate) key_value_cfgs: FxHashMap<String, String>, | 39 | pub(crate) key_value_cfgs: FxHashMap<String, String>, |
40 | |||
25 | pub(crate) out_dir: Option<PathBuf>, | 41 | pub(crate) out_dir: Option<PathBuf>, |
26 | pub(crate) proc_macro_dylib_path: Option<PathBuf>, | 42 | pub(crate) proc_macro_dylib_path: Option<PathBuf>, |
27 | } | 43 | } |
@@ -48,9 +64,72 @@ pub struct Dep { | |||
48 | pub(crate) name: String, | 64 | pub(crate) name: String, |
49 | } | 65 | } |
50 | 66 | ||
51 | /// Roots and crates that compose this Rust project. | 67 | #[cfg(test)] |
52 | #[derive(Clone, Debug, Deserialize)] | 68 | mod tests { |
53 | pub struct JsonProject { | 69 | use super::*; |
54 | pub(crate) roots: Vec<Root>, | 70 | use serde_json::json; |
55 | pub(crate) crates: Vec<Crate>, | 71 | |
72 | #[test] | ||
73 | fn test_crate_deserialization() { | ||
74 | let raw_json = json!( { | ||
75 | "crate_id": 2, | ||
76 | "root_module": "this/is/a/file/path.rs", | ||
77 | "deps": [ | ||
78 | { | ||
79 | "crate": 1, | ||
80 | "name": "some_dep_crate" | ||
81 | }, | ||
82 | ], | ||
83 | "edition": "2015", | ||
84 | "cfg": [ | ||
85 | "atom_1", | ||
86 | "atom_2", | ||
87 | "feature=feature_1", | ||
88 | "feature=feature_2", | ||
89 | "other=value", | ||
90 | ], | ||
91 | |||
92 | }); | ||
93 | |||
94 | let krate: Crate = serde_json::from_value(raw_json).unwrap(); | ||
95 | |||
96 | assert!(krate.cfg.contains(&"atom_1".to_string())); | ||
97 | assert!(krate.cfg.contains(&"atom_2".to_string())); | ||
98 | assert!(krate.cfg.contains(&"feature=feature_1".to_string())); | ||
99 | assert!(krate.cfg.contains(&"feature=feature_2".to_string())); | ||
100 | assert!(krate.cfg.contains(&"other=value".to_string())); | ||
101 | } | ||
102 | |||
103 | #[test] | ||
104 | fn test_crate_deserialization_old_json() { | ||
105 | let raw_json = json!( { | ||
106 | "crate_id": 2, | ||
107 | "root_module": "this/is/a/file/path.rs", | ||
108 | "deps": [ | ||
109 | { | ||
110 | "crate": 1, | ||
111 | "name": "some_dep_crate" | ||
112 | }, | ||
113 | ], | ||
114 | "edition": "2015", | ||
115 | "atom_cfgs": [ | ||
116 | "atom_1", | ||
117 | "atom_2", | ||
118 | ], | ||
119 | "key_value_cfgs": { | ||
120 | "feature": "feature_1", | ||
121 | "feature": "feature_2", | ||
122 | "other": "value", | ||
123 | }, | ||
124 | }); | ||
125 | |||
126 | let krate: Crate = serde_json::from_value(raw_json).unwrap(); | ||
127 | |||
128 | assert!(krate.atom_cfgs.contains(&"atom_1".to_string())); | ||
129 | assert!(krate.atom_cfgs.contains(&"atom_2".to_string())); | ||
130 | assert!(krate.key_value_cfgs.contains_key(&"feature".to_string())); | ||
131 | assert_eq!(krate.key_value_cfgs.get("feature"), Some(&"feature_2".to_string())); | ||
132 | assert!(krate.key_value_cfgs.contains_key(&"other".to_string())); | ||
133 | assert_eq!(krate.key_value_cfgs.get("other"), Some(&"value".to_string())); | ||
134 | } | ||
56 | } | 135 | } |
diff --git a/crates/ra_project_model/src/lib.rs b/crates/ra_project_model/src/lib.rs index a2e9f65ef..a99612690 100644 --- a/crates/ra_project_model/src/lib.rs +++ b/crates/ra_project_model/src/lib.rs | |||
@@ -14,7 +14,7 @@ use std::{ | |||
14 | use anyhow::{bail, Context, Result}; | 14 | use anyhow::{bail, Context, Result}; |
15 | use ra_cfg::CfgOptions; | 15 | use ra_cfg::CfgOptions; |
16 | use ra_db::{CrateGraph, CrateName, Edition, Env, ExternSource, ExternSourceId, FileId}; | 16 | use ra_db::{CrateGraph, CrateName, Edition, Env, ExternSource, ExternSourceId, FileId}; |
17 | use rustc_hash::FxHashMap; | 17 | use rustc_hash::{FxHashMap, FxHashSet}; |
18 | use serde_json::from_reader; | 18 | use serde_json::from_reader; |
19 | 19 | ||
20 | pub use crate::{ | 20 | pub use crate::{ |
@@ -57,25 +57,25 @@ impl PackageRoot { | |||
57 | } | 57 | } |
58 | } | 58 | } |
59 | 59 | ||
60 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] | 60 | #[derive(Debug, Clone, PartialEq, Eq, Hash, Ord, PartialOrd)] |
61 | pub enum ProjectRoot { | 61 | pub enum ProjectManifest { |
62 | ProjectJson(PathBuf), | 62 | ProjectJson(PathBuf), |
63 | CargoToml(PathBuf), | 63 | CargoToml(PathBuf), |
64 | } | 64 | } |
65 | 65 | ||
66 | impl ProjectRoot { | 66 | impl ProjectManifest { |
67 | pub fn from_manifest_file(path: PathBuf) -> Result<ProjectRoot> { | 67 | pub fn from_manifest_file(path: PathBuf) -> Result<ProjectManifest> { |
68 | if path.ends_with("rust-project.json") { | 68 | if path.ends_with("rust-project.json") { |
69 | return Ok(ProjectRoot::ProjectJson(path)); | 69 | return Ok(ProjectManifest::ProjectJson(path)); |
70 | } | 70 | } |
71 | if path.ends_with("Cargo.toml") { | 71 | if path.ends_with("Cargo.toml") { |
72 | return Ok(ProjectRoot::CargoToml(path)); | 72 | return Ok(ProjectManifest::CargoToml(path)); |
73 | } | 73 | } |
74 | bail!("project root must point to Cargo.toml or rust-project.json: {}", path.display()) | 74 | bail!("project root must point to Cargo.toml or rust-project.json: {}", path.display()) |
75 | } | 75 | } |
76 | 76 | ||
77 | pub fn discover_single(path: &Path) -> Result<ProjectRoot> { | 77 | pub fn discover_single(path: &Path) -> Result<ProjectManifest> { |
78 | let mut candidates = ProjectRoot::discover(path)?; | 78 | let mut candidates = ProjectManifest::discover(path)?; |
79 | let res = match candidates.pop() { | 79 | let res = match candidates.pop() { |
80 | None => bail!("no projects"), | 80 | None => bail!("no projects"), |
81 | Some(it) => it, | 81 | Some(it) => it, |
@@ -87,12 +87,12 @@ impl ProjectRoot { | |||
87 | Ok(res) | 87 | Ok(res) |
88 | } | 88 | } |
89 | 89 | ||
90 | pub fn discover(path: &Path) -> io::Result<Vec<ProjectRoot>> { | 90 | pub fn discover(path: &Path) -> io::Result<Vec<ProjectManifest>> { |
91 | if let Some(project_json) = find_in_parent_dirs(path, "rust-project.json") { | 91 | if let Some(project_json) = find_in_parent_dirs(path, "rust-project.json") { |
92 | return Ok(vec![ProjectRoot::ProjectJson(project_json)]); | 92 | return Ok(vec![ProjectManifest::ProjectJson(project_json)]); |
93 | } | 93 | } |
94 | return find_cargo_toml(path) | 94 | return find_cargo_toml(path) |
95 | .map(|paths| paths.into_iter().map(ProjectRoot::CargoToml).collect()); | 95 | .map(|paths| paths.into_iter().map(ProjectManifest::CargoToml).collect()); |
96 | 96 | ||
97 | fn find_cargo_toml(path: &Path) -> io::Result<Vec<PathBuf>> { | 97 | fn find_cargo_toml(path: &Path) -> io::Result<Vec<PathBuf>> { |
98 | match find_in_parent_dirs(path, "Cargo.toml") { | 98 | match find_in_parent_dirs(path, "Cargo.toml") { |
@@ -128,16 +128,28 @@ impl ProjectRoot { | |||
128 | .collect() | 128 | .collect() |
129 | } | 129 | } |
130 | } | 130 | } |
131 | |||
132 | pub fn discover_all(paths: &[impl AsRef<Path>]) -> Vec<ProjectManifest> { | ||
133 | let mut res = paths | ||
134 | .iter() | ||
135 | .filter_map(|it| ProjectManifest::discover(it.as_ref()).ok()) | ||
136 | .flatten() | ||
137 | .collect::<FxHashSet<_>>() | ||
138 | .into_iter() | ||
139 | .collect::<Vec<_>>(); | ||
140 | res.sort(); | ||
141 | res | ||
142 | } | ||
131 | } | 143 | } |
132 | 144 | ||
133 | impl ProjectWorkspace { | 145 | impl ProjectWorkspace { |
134 | pub fn load( | 146 | pub fn load( |
135 | root: ProjectRoot, | 147 | root: ProjectManifest, |
136 | cargo_features: &CargoConfig, | 148 | cargo_features: &CargoConfig, |
137 | with_sysroot: bool, | 149 | with_sysroot: bool, |
138 | ) -> Result<ProjectWorkspace> { | 150 | ) -> Result<ProjectWorkspace> { |
139 | let res = match root { | 151 | let res = match root { |
140 | ProjectRoot::ProjectJson(project_json) => { | 152 | ProjectManifest::ProjectJson(project_json) => { |
141 | let file = File::open(&project_json).with_context(|| { | 153 | let file = File::open(&project_json).with_context(|| { |
142 | format!("Failed to open json file {}", project_json.display()) | 154 | format!("Failed to open json file {}", project_json.display()) |
143 | })?; | 155 | })?; |
@@ -148,7 +160,7 @@ impl ProjectWorkspace { | |||
148 | })?, | 160 | })?, |
149 | } | 161 | } |
150 | } | 162 | } |
151 | ProjectRoot::CargoToml(cargo_toml) => { | 163 | ProjectManifest::CargoToml(cargo_toml) => { |
152 | let cargo = CargoWorkspace::from_cargo_metadata(&cargo_toml, cargo_features) | 164 | let cargo = CargoWorkspace::from_cargo_metadata(&cargo_toml, cargo_features) |
153 | .with_context(|| { | 165 | .with_context(|| { |
154 | format!( | 166 | format!( |
@@ -252,6 +264,16 @@ impl ProjectWorkspace { | |||
252 | }; | 264 | }; |
253 | let cfg_options = { | 265 | let cfg_options = { |
254 | let mut opts = default_cfg_options.clone(); | 266 | let mut opts = default_cfg_options.clone(); |
267 | for cfg in &krate.cfg { | ||
268 | match cfg.find('=') { | ||
269 | None => opts.insert_atom(cfg.into()), | ||
270 | Some(pos) => { | ||
271 | let key = &cfg[..pos]; | ||
272 | let value = cfg[pos + 1..].trim_matches('"'); | ||
273 | opts.insert_key_value(key.into(), value.into()); | ||
274 | } | ||
275 | } | ||
276 | } | ||
255 | for name in &krate.atom_cfgs { | 277 | for name in &krate.atom_cfgs { |
256 | opts.insert_atom(name.into()); | 278 | opts.insert_atom(name.into()); |
257 | } | 279 | } |