aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_project_model
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_project_model')
-rw-r--r--crates/ra_project_model/src/json_project.rs89
-rw-r--r--crates/ra_project_model/src/lib.rs60
2 files changed, 128 insertions, 21 deletions
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;
5use rustc_hash::{FxHashMap, FxHashSet}; 5use rustc_hash::{FxHashMap, FxHashSet};
6use serde::Deserialize; 6use serde::Deserialize;
7 7
8/// Roots and crates that compose this Rust project.
9#[derive(Clone, Debug, Deserialize)]
10pub 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)] 68mod tests {
53pub 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..7ad941279 100644
--- a/crates/ra_project_model/src/lib.rs
+++ b/crates/ra_project_model/src/lib.rs
@@ -14,7 +14,7 @@ use std::{
14use anyhow::{bail, Context, Result}; 14use anyhow::{bail, Context, Result};
15use ra_cfg::CfgOptions; 15use ra_cfg::CfgOptions;
16use ra_db::{CrateGraph, CrateName, Edition, Env, ExternSource, ExternSourceId, FileId}; 16use ra_db::{CrateGraph, CrateName, Edition, Env, ExternSource, ExternSourceId, FileId};
17use rustc_hash::FxHashMap; 17use rustc_hash::{FxHashMap, FxHashSet};
18use serde_json::from_reader; 18use serde_json::from_reader;
19 19
20pub use crate::{ 20pub use crate::{
@@ -32,6 +32,12 @@ pub enum ProjectWorkspace {
32 Json { project: JsonProject }, 32 Json { project: JsonProject },
33} 33}
34 34
35impl From<JsonProject> for ProjectWorkspace {
36 fn from(project: JsonProject) -> ProjectWorkspace {
37 ProjectWorkspace::Json { project }
38 }
39}
40
35/// `PackageRoot` describes a package root folder. 41/// `PackageRoot` describes a package root folder.
36/// Which may be an external dependency, or a member of 42/// Which may be an external dependency, or a member of
37/// the current workspace. 43/// the current workspace.
@@ -57,25 +63,25 @@ impl PackageRoot {
57 } 63 }
58} 64}
59 65
60#[derive(Debug, Clone, PartialEq, Eq, Hash)] 66#[derive(Debug, Clone, PartialEq, Eq, Hash, Ord, PartialOrd)]
61pub enum ProjectRoot { 67pub enum ProjectManifest {
62 ProjectJson(PathBuf), 68 ProjectJson(PathBuf),
63 CargoToml(PathBuf), 69 CargoToml(PathBuf),
64} 70}
65 71
66impl ProjectRoot { 72impl ProjectManifest {
67 pub fn from_manifest_file(path: PathBuf) -> Result<ProjectRoot> { 73 pub fn from_manifest_file(path: PathBuf) -> Result<ProjectManifest> {
68 if path.ends_with("rust-project.json") { 74 if path.ends_with("rust-project.json") {
69 return Ok(ProjectRoot::ProjectJson(path)); 75 return Ok(ProjectManifest::ProjectJson(path));
70 } 76 }
71 if path.ends_with("Cargo.toml") { 77 if path.ends_with("Cargo.toml") {
72 return Ok(ProjectRoot::CargoToml(path)); 78 return Ok(ProjectManifest::CargoToml(path));
73 } 79 }
74 bail!("project root must point to Cargo.toml or rust-project.json: {}", path.display()) 80 bail!("project root must point to Cargo.toml or rust-project.json: {}", path.display())
75 } 81 }
76 82
77 pub fn discover_single(path: &Path) -> Result<ProjectRoot> { 83 pub fn discover_single(path: &Path) -> Result<ProjectManifest> {
78 let mut candidates = ProjectRoot::discover(path)?; 84 let mut candidates = ProjectManifest::discover(path)?;
79 let res = match candidates.pop() { 85 let res = match candidates.pop() {
80 None => bail!("no projects"), 86 None => bail!("no projects"),
81 Some(it) => it, 87 Some(it) => it,
@@ -87,12 +93,12 @@ impl ProjectRoot {
87 Ok(res) 93 Ok(res)
88 } 94 }
89 95
90 pub fn discover(path: &Path) -> io::Result<Vec<ProjectRoot>> { 96 pub fn discover(path: &Path) -> io::Result<Vec<ProjectManifest>> {
91 if let Some(project_json) = find_in_parent_dirs(path, "rust-project.json") { 97 if let Some(project_json) = find_in_parent_dirs(path, "rust-project.json") {
92 return Ok(vec![ProjectRoot::ProjectJson(project_json)]); 98 return Ok(vec![ProjectManifest::ProjectJson(project_json)]);
93 } 99 }
94 return find_cargo_toml(path) 100 return find_cargo_toml(path)
95 .map(|paths| paths.into_iter().map(ProjectRoot::CargoToml).collect()); 101 .map(|paths| paths.into_iter().map(ProjectManifest::CargoToml).collect());
96 102
97 fn find_cargo_toml(path: &Path) -> io::Result<Vec<PathBuf>> { 103 fn find_cargo_toml(path: &Path) -> io::Result<Vec<PathBuf>> {
98 match find_in_parent_dirs(path, "Cargo.toml") { 104 match find_in_parent_dirs(path, "Cargo.toml") {
@@ -128,16 +134,28 @@ impl ProjectRoot {
128 .collect() 134 .collect()
129 } 135 }
130 } 136 }
137
138 pub fn discover_all(paths: &[impl AsRef<Path>]) -> Vec<ProjectManifest> {
139 let mut res = paths
140 .iter()
141 .filter_map(|it| ProjectManifest::discover(it.as_ref()).ok())
142 .flatten()
143 .collect::<FxHashSet<_>>()
144 .into_iter()
145 .collect::<Vec<_>>();
146 res.sort();
147 res
148 }
131} 149}
132 150
133impl ProjectWorkspace { 151impl ProjectWorkspace {
134 pub fn load( 152 pub fn load(
135 root: ProjectRoot, 153 manifest: ProjectManifest,
136 cargo_features: &CargoConfig, 154 cargo_features: &CargoConfig,
137 with_sysroot: bool, 155 with_sysroot: bool,
138 ) -> Result<ProjectWorkspace> { 156 ) -> Result<ProjectWorkspace> {
139 let res = match root { 157 let res = match manifest {
140 ProjectRoot::ProjectJson(project_json) => { 158 ProjectManifest::ProjectJson(project_json) => {
141 let file = File::open(&project_json).with_context(|| { 159 let file = File::open(&project_json).with_context(|| {
142 format!("Failed to open json file {}", project_json.display()) 160 format!("Failed to open json file {}", project_json.display())
143 })?; 161 })?;
@@ -148,7 +166,7 @@ impl ProjectWorkspace {
148 })?, 166 })?,
149 } 167 }
150 } 168 }
151 ProjectRoot::CargoToml(cargo_toml) => { 169 ProjectManifest::CargoToml(cargo_toml) => {
152 let cargo = CargoWorkspace::from_cargo_metadata(&cargo_toml, cargo_features) 170 let cargo = CargoWorkspace::from_cargo_metadata(&cargo_toml, cargo_features)
153 .with_context(|| { 171 .with_context(|| {
154 format!( 172 format!(
@@ -252,6 +270,16 @@ impl ProjectWorkspace {
252 }; 270 };
253 let cfg_options = { 271 let cfg_options = {
254 let mut opts = default_cfg_options.clone(); 272 let mut opts = default_cfg_options.clone();
273 for cfg in &krate.cfg {
274 match cfg.find('=') {
275 None => opts.insert_atom(cfg.into()),
276 Some(pos) => {
277 let key = &cfg[..pos];
278 let value = cfg[pos + 1..].trim_matches('"');
279 opts.insert_key_value(key.into(), value.into());
280 }
281 }
282 }
255 for name in &krate.atom_cfgs { 283 for name in &krate.atom_cfgs {
256 opts.insert_atom(name.into()); 284 opts.insert_atom(name.into());
257 } 285 }