aboutsummaryrefslogtreecommitdiff
path: root/crates/project_model/src/project_json.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/project_model/src/project_json.rs')
-rw-r--r--crates/project_model/src/project_json.rs143
1 files changed, 143 insertions, 0 deletions
diff --git a/crates/project_model/src/project_json.rs b/crates/project_model/src/project_json.rs
new file mode 100644
index 000000000..060ea5b7d
--- /dev/null
+++ b/crates/project_model/src/project_json.rs
@@ -0,0 +1,143 @@
1//! FIXME: write short doc here
2
3use std::path::PathBuf;
4
5use base_db::{CrateId, CrateName, Dependency, Edition};
6use paths::{AbsPath, AbsPathBuf};
7use rustc_hash::FxHashMap;
8use serde::{de, Deserialize};
9
10use crate::cfg_flag::CfgFlag;
11
12/// Roots and crates that compose this Rust project.
13#[derive(Clone, Debug, Eq, PartialEq)]
14pub struct ProjectJson {
15 pub(crate) crates: Vec<Crate>,
16}
17
18/// A crate points to the root module of a crate and lists the dependencies of the crate. This is
19/// useful in creating the crate graph.
20#[derive(Clone, Debug, Eq, PartialEq)]
21pub struct Crate {
22 pub(crate) root_module: AbsPathBuf,
23 pub(crate) edition: Edition,
24 pub(crate) deps: Vec<Dependency>,
25 pub(crate) cfg: Vec<CfgFlag>,
26 pub(crate) target: Option<String>,
27 pub(crate) env: FxHashMap<String, String>,
28 pub(crate) proc_macro_dylib_path: Option<AbsPathBuf>,
29 pub(crate) is_workspace_member: bool,
30 pub(crate) include: Vec<AbsPathBuf>,
31 pub(crate) exclude: Vec<AbsPathBuf>,
32}
33
34impl ProjectJson {
35 pub fn new(base: &AbsPath, data: ProjectJsonData) -> ProjectJson {
36 ProjectJson {
37 crates: data
38 .crates
39 .into_iter()
40 .map(|crate_data| {
41 let is_workspace_member = crate_data.is_workspace_member.unwrap_or_else(|| {
42 crate_data.root_module.is_relative()
43 && !crate_data.root_module.starts_with("..")
44 || crate_data.root_module.starts_with(base)
45 });
46 let root_module = base.join(crate_data.root_module);
47 let (include, exclude) = match crate_data.source {
48 Some(src) => {
49 let absolutize = |dirs: Vec<PathBuf>| {
50 dirs.into_iter().map(|it| base.join(it)).collect::<Vec<_>>()
51 };
52 (absolutize(src.include_dirs), absolutize(src.exclude_dirs))
53 }
54 None => (vec![root_module.parent().unwrap().to_path_buf()], Vec::new()),
55 };
56
57 Crate {
58 root_module,
59 edition: crate_data.edition.into(),
60 deps: crate_data
61 .deps
62 .into_iter()
63 .map(|dep_data| Dependency {
64 crate_id: CrateId(dep_data.krate as u32),
65 name: dep_data.name,
66 })
67 .collect::<Vec<_>>(),
68 cfg: crate_data.cfg,
69 target: crate_data.target,
70 env: crate_data.env,
71 proc_macro_dylib_path: crate_data
72 .proc_macro_dylib_path
73 .map(|it| base.join(it)),
74 is_workspace_member,
75 include,
76 exclude,
77 }
78 })
79 .collect::<Vec<_>>(),
80 }
81 }
82}
83
84#[derive(Deserialize)]
85pub struct ProjectJsonData {
86 crates: Vec<CrateData>,
87}
88
89#[derive(Deserialize)]
90struct CrateData {
91 root_module: PathBuf,
92 edition: EditionData,
93 deps: Vec<DepData>,
94 #[serde(default)]
95 cfg: Vec<CfgFlag>,
96 target: Option<String>,
97 #[serde(default)]
98 env: FxHashMap<String, String>,
99 proc_macro_dylib_path: Option<PathBuf>,
100 is_workspace_member: Option<bool>,
101 source: Option<CrateSource>,
102}
103
104#[derive(Deserialize)]
105#[serde(rename = "edition")]
106enum EditionData {
107 #[serde(rename = "2015")]
108 Edition2015,
109 #[serde(rename = "2018")]
110 Edition2018,
111}
112
113impl From<EditionData> for Edition {
114 fn from(data: EditionData) -> Self {
115 match data {
116 EditionData::Edition2015 => Edition::Edition2015,
117 EditionData::Edition2018 => Edition::Edition2018,
118 }
119 }
120}
121
122#[derive(Deserialize)]
123struct DepData {
124 /// Identifies a crate by position in the crates array.
125 #[serde(rename = "crate")]
126 krate: usize,
127 #[serde(deserialize_with = "deserialize_crate_name")]
128 name: CrateName,
129}
130
131#[derive(Deserialize)]
132struct CrateSource {
133 include_dirs: Vec<PathBuf>,
134 exclude_dirs: Vec<PathBuf>,
135}
136
137fn deserialize_crate_name<'de, D>(de: D) -> Result<CrateName, D::Error>
138where
139 D: de::Deserializer<'de>,
140{
141 let name = String::deserialize(de)?;
142 CrateName::new(&name).map_err(|err| de::Error::custom(format!("invalid crate name: {:?}", err)))
143}