aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_lsp_server/src/project_model.rs
diff options
context:
space:
mode:
authorAleksey Kladov <[email protected]>2018-09-16 10:54:24 +0100
committerAleksey Kladov <[email protected]>2018-09-16 11:07:39 +0100
commitb5021411a84822cb3f1e3aeffad9550dd15bdeb6 (patch)
tree9dca564f8e51b298dced01c4ce669c756dce3142 /crates/ra_lsp_server/src/project_model.rs
parentba0bfeee12e19da40b5eabc8d0408639af10e96f (diff)
rename all things
Diffstat (limited to 'crates/ra_lsp_server/src/project_model.rs')
-rw-r--r--crates/ra_lsp_server/src/project_model.rs175
1 files changed, 175 insertions, 0 deletions
diff --git a/crates/ra_lsp_server/src/project_model.rs b/crates/ra_lsp_server/src/project_model.rs
new file mode 100644
index 000000000..5db34e3e5
--- /dev/null
+++ b/crates/ra_lsp_server/src/project_model.rs
@@ -0,0 +1,175 @@
1use std::{
2 collections::{HashMap, HashSet},
3 path::{Path, PathBuf},
4};
5use cargo_metadata::{metadata_run, CargoOpt};
6use ra_syntax::SmolStr;
7
8use {
9 Result,
10 thread_watcher::{Worker, ThreadWatcher},
11};
12
13#[derive(Debug, Clone)]
14pub struct CargoWorkspace {
15 packages: Vec<PackageData>,
16 targets: Vec<TargetData>,
17}
18
19#[derive(Clone, Copy, Debug, Serialize)]
20pub struct Package(usize);
21#[derive(Clone, Copy, Debug, Serialize)]
22pub struct Target(usize);
23
24#[derive(Debug, Clone)]
25struct PackageData {
26 name: SmolStr,
27 manifest: PathBuf,
28 targets: Vec<Target>,
29 is_member: bool,
30}
31
32#[derive(Debug, Clone)]
33struct TargetData {
34 pkg: Package,
35 name: SmolStr,
36 root: PathBuf,
37 kind: TargetKind,
38}
39
40#[derive(Debug, Clone, Copy, PartialEq, Eq)]
41pub enum TargetKind {
42 Bin, Lib, Example, Test, Bench, Other,
43}
44
45impl Package {
46 pub fn name(self, ws: &CargoWorkspace) -> &str {
47 ws.pkg(self).name.as_str()
48 }
49 pub fn root(self, ws: &CargoWorkspace) -> &Path {
50 ws.pkg(self).manifest.parent().unwrap()
51 }
52 pub fn targets<'a>(self, ws: &'a CargoWorkspace) -> impl Iterator<Item=Target> + 'a {
53 ws.pkg(self).targets.iter().cloned()
54 }
55 pub fn is_member(self, ws: &CargoWorkspace) -> bool {
56 ws.pkg(self).is_member
57 }
58}
59
60impl Target {
61 pub fn package(self, ws: &CargoWorkspace) -> Package {
62 ws.tgt(self).pkg
63 }
64 pub fn name(self, ws: &CargoWorkspace) -> &str {
65 ws.tgt(self).name.as_str()
66 }
67 pub fn root(self, ws: &CargoWorkspace) -> &Path {
68 ws.tgt(self).root.as_path()
69 }
70 pub fn kind(self, ws: &CargoWorkspace) -> TargetKind {
71 ws.tgt(self).kind
72 }
73}
74
75impl CargoWorkspace {
76 pub fn from_cargo_metadata(path: &Path) -> Result<CargoWorkspace> {
77 let cargo_toml = find_cargo_toml(path)?;
78 let meta = metadata_run(
79 Some(cargo_toml.as_path()),
80 true,
81 Some(CargoOpt::AllFeatures)
82 ).map_err(|e| format_err!("cargo metadata failed: {}", e))?;
83 let mut pkg_by_id = HashMap::new();
84 let mut packages = Vec::new();
85 let mut targets = Vec::new();
86
87 let ws_members: HashSet<String> = meta.workspace_members
88 .into_iter()
89 .map(|it| it.raw)
90 .collect();
91
92 for meta_pkg in meta.packages {
93 let pkg = Package(packages.len());
94 let is_member = ws_members.contains(&meta_pkg.id);
95 pkg_by_id.insert(meta_pkg.id.clone(), pkg);
96 let mut pkg_data = PackageData {
97 name: meta_pkg.name.into(),
98 manifest: PathBuf::from(meta_pkg.manifest_path),
99 targets: Vec::new(),
100 is_member,
101 };
102 for meta_tgt in meta_pkg.targets {
103 let tgt = Target(targets.len());
104 targets.push(TargetData {
105 pkg,
106 name: meta_tgt.name.into(),
107 root: PathBuf::from(meta_tgt.src_path),
108 kind: TargetKind::new(meta_tgt.kind.as_slice()),
109 });
110 pkg_data.targets.push(tgt);
111 }
112 packages.push(pkg_data)
113 }
114
115 Ok(CargoWorkspace { packages, targets })
116 }
117 pub fn packages<'a>(&'a self) -> impl Iterator<Item=Package> + 'a {
118 (0..self.packages.len()).map(Package)
119 }
120 pub fn target_by_root(&self, root: &Path) -> Option<Target> {
121 self.packages()
122 .filter_map(|pkg| pkg.targets(self).find(|it| it.root(self) == root))
123 .next()
124 }
125 fn pkg(&self, pkg: Package) -> &PackageData {
126 &self.packages[pkg.0]
127 }
128 fn tgt(&self, tgt: Target) -> &TargetData {
129 &self.targets[tgt.0]
130 }
131}
132
133fn find_cargo_toml(path: &Path) -> Result<PathBuf> {
134 if path.ends_with("Cargo.toml") {
135 return Ok(path.to_path_buf());
136 }
137 let mut curr = Some(path);
138 while let Some(path) = curr {
139 let candidate = path.join("Cargo.toml");
140 if candidate.exists() {
141 return Ok(candidate);
142 }
143 curr = path.parent();
144 }
145 bail!("can't find Cargo.toml at {}", path.display())
146}
147
148impl TargetKind {
149 fn new(kinds: &[String]) -> TargetKind {
150 for kind in kinds {
151 return match kind.as_str() {
152 "bin" => TargetKind::Bin,
153 "test" => TargetKind::Test,
154 "bench" => TargetKind::Bench,
155 "example" => TargetKind::Example,
156 _ if kind.contains("lib") => TargetKind::Lib,
157 _ => continue,
158 }
159 }
160 TargetKind::Other
161 }
162}
163
164pub fn workspace_loader() -> (Worker<PathBuf, Result<CargoWorkspace>>, ThreadWatcher) {
165 Worker::<PathBuf, Result<CargoWorkspace>>::spawn(
166 "workspace loader",
167 1,
168 |input_receiver, output_sender| {
169 input_receiver
170 .into_iter()
171 .map(|path| CargoWorkspace::from_cargo_metadata(path.as_path()))
172 .for_each(|it| output_sender.send(it))
173 }
174 )
175}