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.rs130
1 files changed, 130 insertions, 0 deletions
diff --git a/crates/ra_project_model/src/lib.rs b/crates/ra_project_model/src/lib.rs
new file mode 100644
index 000000000..3b1e07149
--- /dev/null
+++ b/crates/ra_project_model/src/lib.rs
@@ -0,0 +1,130 @@
1mod cargo_workspace;
2mod sysroot;
3
4use std::path::{Path, PathBuf};
5
6use failure::bail;
7use rustc_hash::FxHashMap;
8
9use ra_db::{CrateGraph, FileId};
10
11pub use crate::{
12 cargo_workspace::{CargoWorkspace, Package, Target, TargetKind},
13 sysroot::Sysroot,
14};
15
16// TODO use proper error enum
17pub type Result<T> = ::std::result::Result<T, ::failure::Error>;
18
19#[derive(Debug, Clone)]
20pub struct ProjectWorkspace {
21 pub cargo: CargoWorkspace,
22 pub sysroot: Sysroot,
23}
24
25impl ProjectWorkspace {
26 pub fn discover(path: &Path) -> Result<ProjectWorkspace> {
27 let cargo_toml = find_cargo_toml(path)?;
28 let cargo = CargoWorkspace::from_cargo_metadata(&cargo_toml)?;
29 let sysroot = Sysroot::discover(&cargo_toml)?;
30 let res = ProjectWorkspace { cargo, sysroot };
31 Ok(res)
32 }
33
34 pub fn to_crate_graph(&self, load: &mut dyn FnMut(&Path) -> Option<FileId>) -> CrateGraph {
35 let mut crate_graph = CrateGraph::default();
36 let mut sysroot_crates = FxHashMap::default();
37 for krate in self.sysroot.crates() {
38 if let Some(file_id) = load(krate.root(&self.sysroot)) {
39 sysroot_crates.insert(krate, crate_graph.add_crate_root(file_id));
40 }
41 }
42 for from in self.sysroot.crates() {
43 for to in from.deps(&self.sysroot) {
44 let name = to.name(&self.sysroot);
45 if let (Some(&from), Some(&to)) =
46 (sysroot_crates.get(&from), sysroot_crates.get(&to))
47 {
48 if let Err(_) = crate_graph.add_dep(from, name.into(), to) {
49 log::error!("cyclic dependency between sysroot crates")
50 }
51 }
52 }
53 }
54
55 let libstd = self.sysroot.std().and_then(|it| sysroot_crates.get(&it).map(|&it| it));
56
57 let mut pkg_to_lib_crate = FxHashMap::default();
58 let mut pkg_crates = FxHashMap::default();
59 // Next, create crates for each package, target pair
60 for pkg in self.cargo.packages() {
61 let mut lib_tgt = None;
62 for tgt in pkg.targets(&self.cargo) {
63 let root = tgt.root(&self.cargo);
64 if let Some(file_id) = load(root) {
65 let crate_id = crate_graph.add_crate_root(file_id);
66 if tgt.kind(&self.cargo) == TargetKind::Lib {
67 lib_tgt = Some(crate_id);
68 pkg_to_lib_crate.insert(pkg, crate_id);
69 }
70 pkg_crates.entry(pkg).or_insert_with(Vec::new).push(crate_id);
71 }
72 }
73
74 // Set deps to the std and to the lib target of the current package
75 for &from in pkg_crates.get(&pkg).into_iter().flatten() {
76 if let Some(to) = lib_tgt {
77 if to != from {
78 if let Err(_) = crate_graph.add_dep(from, pkg.name(&self.cargo).into(), to)
79 {
80 log::error!(
81 "cyclic dependency between targets of {}",
82 pkg.name(&self.cargo)
83 )
84 }
85 }
86 }
87 if let Some(std) = libstd {
88 if let Err(_) = crate_graph.add_dep(from, "std".into(), std) {
89 log::error!("cyclic dependency on std for {}", pkg.name(&self.cargo))
90 }
91 }
92 }
93 }
94
95 // Now add a dep ednge from all targets of upstream to the lib
96 // target of downstream.
97 for pkg in self.cargo.packages() {
98 for dep in pkg.dependencies(&self.cargo) {
99 if let Some(&to) = pkg_to_lib_crate.get(&dep.pkg) {
100 for &from in pkg_crates.get(&pkg).into_iter().flatten() {
101 if let Err(_) = crate_graph.add_dep(from, dep.name.clone().into(), to) {
102 log::error!(
103 "cyclic dependency {} -> {}",
104 pkg.name(&self.cargo),
105 dep.pkg.name(&self.cargo)
106 )
107 }
108 }
109 }
110 }
111 }
112
113 crate_graph
114 }
115}
116
117fn find_cargo_toml(path: &Path) -> Result<PathBuf> {
118 if path.ends_with("Cargo.toml") {
119 return Ok(path.to_path_buf());
120 }
121 let mut curr = Some(path);
122 while let Some(path) = curr {
123 let candidate = path.join("Cargo.toml");
124 if candidate.exists() {
125 return Ok(candidate);
126 }
127 curr = path.parent();
128 }
129 bail!("can't find Cargo.toml at {}", path.display())
130}