diff options
Diffstat (limited to 'crates')
-rw-r--r-- | crates/paths/src/lib.rs | 7 | ||||
-rw-r--r-- | crates/project_model/src/build_data.rs | 86 | ||||
-rw-r--r-- | crates/project_model/src/workspace.rs | 10 | ||||
-rw-r--r-- | crates/rust-analyzer/src/reload.rs | 41 | ||||
-rw-r--r-- | crates/stdx/src/lib.rs | 8 |
5 files changed, 110 insertions, 42 deletions
diff --git a/crates/paths/src/lib.rs b/crates/paths/src/lib.rs index 22011cb33..f09ad37e3 100644 --- a/crates/paths/src/lib.rs +++ b/crates/paths/src/lib.rs | |||
@@ -1,6 +1,7 @@ | |||
1 | //! Thin wrappers around `std::path`, distinguishing between absolute and | 1 | //! Thin wrappers around `std::path`, distinguishing between absolute and |
2 | //! relative paths. | 2 | //! relative paths. |
3 | use std::{ | 3 | use std::{ |
4 | borrow::Borrow, | ||
4 | convert::{TryFrom, TryInto}, | 5 | convert::{TryFrom, TryInto}, |
5 | ops, | 6 | ops, |
6 | path::{Component, Path, PathBuf}, | 7 | path::{Component, Path, PathBuf}, |
@@ -35,6 +36,12 @@ impl AsRef<AbsPath> for AbsPathBuf { | |||
35 | } | 36 | } |
36 | } | 37 | } |
37 | 38 | ||
39 | impl Borrow<AbsPath> for AbsPathBuf { | ||
40 | fn borrow(&self) -> &AbsPath { | ||
41 | self.as_path() | ||
42 | } | ||
43 | } | ||
44 | |||
38 | impl TryFrom<PathBuf> for AbsPathBuf { | 45 | impl TryFrom<PathBuf> for AbsPathBuf { |
39 | type Error = PathBuf; | 46 | type Error = PathBuf; |
40 | fn try_from(path_buf: PathBuf) -> Result<AbsPathBuf, PathBuf> { | 47 | fn try_from(path_buf: PathBuf) -> Result<AbsPathBuf, PathBuf> { |
diff --git a/crates/project_model/src/build_data.rs b/crates/project_model/src/build_data.rs index c2c87b207..0d4d39fef 100644 --- a/crates/project_model/src/build_data.rs +++ b/crates/project_model/src/build_data.rs | |||
@@ -13,12 +13,12 @@ use cargo_metadata::{BuildScript, Message}; | |||
13 | use itertools::Itertools; | 13 | use itertools::Itertools; |
14 | use paths::{AbsPath, AbsPathBuf}; | 14 | use paths::{AbsPath, AbsPathBuf}; |
15 | use rustc_hash::FxHashMap; | 15 | use rustc_hash::FxHashMap; |
16 | use stdx::JodChild; | 16 | use stdx::{format_to, JodChild}; |
17 | 17 | ||
18 | use crate::{cfg_flag::CfgFlag, CargoConfig}; | 18 | use crate::{cfg_flag::CfgFlag, CargoConfig}; |
19 | 19 | ||
20 | #[derive(Debug, Clone, Default, PartialEq, Eq)] | 20 | #[derive(Debug, Clone, Default, PartialEq, Eq)] |
21 | pub(crate) struct BuildData { | 21 | pub(crate) struct PackageBuildData { |
22 | /// List of config flags defined by this package's build script | 22 | /// List of config flags defined by this package's build script |
23 | pub(crate) cfgs: Vec<CfgFlag>, | 23 | pub(crate) cfgs: Vec<CfgFlag>, |
24 | /// List of cargo-related environment variables with their value | 24 | /// List of cargo-related environment variables with their value |
@@ -32,6 +32,17 @@ pub(crate) struct BuildData { | |||
32 | pub(crate) proc_macro_dylib_path: Option<AbsPathBuf>, | 32 | pub(crate) proc_macro_dylib_path: Option<AbsPathBuf>, |
33 | } | 33 | } |
34 | 34 | ||
35 | #[derive(Debug, Default, PartialEq, Eq, Clone)] | ||
36 | pub(crate) struct WorkspaceBuildData { | ||
37 | per_package: FxHashMap<String, PackageBuildData>, | ||
38 | error: Option<String>, | ||
39 | } | ||
40 | |||
41 | #[derive(Debug, Default, PartialEq, Eq, Clone)] | ||
42 | pub struct BuildDataResult { | ||
43 | per_workspace: FxHashMap<AbsPathBuf, WorkspaceBuildData>, | ||
44 | } | ||
45 | |||
35 | #[derive(Clone, Debug)] | 46 | #[derive(Clone, Debug)] |
36 | pub(crate) struct BuildDataConfig { | 47 | pub(crate) struct BuildDataConfig { |
37 | cargo_toml: AbsPathBuf, | 48 | cargo_toml: AbsPathBuf, |
@@ -52,13 +63,6 @@ pub struct BuildDataCollector { | |||
52 | configs: FxHashMap<AbsPathBuf, BuildDataConfig>, | 63 | configs: FxHashMap<AbsPathBuf, BuildDataConfig>, |
53 | } | 64 | } |
54 | 65 | ||
55 | #[derive(Debug, Default, PartialEq, Eq, Clone)] | ||
56 | pub struct BuildDataResult { | ||
57 | data: FxHashMap<AbsPathBuf, BuildDataMap>, | ||
58 | } | ||
59 | |||
60 | pub(crate) type BuildDataMap = FxHashMap<String, BuildData>; | ||
61 | |||
62 | impl BuildDataCollector { | 66 | impl BuildDataCollector { |
63 | pub(crate) fn add_config(&mut self, workspace_root: &AbsPath, config: BuildDataConfig) { | 67 | pub(crate) fn add_config(&mut self, workspace_root: &AbsPath, config: BuildDataConfig) { |
64 | self.configs.insert(workspace_root.to_path_buf(), config); | 68 | self.configs.insert(workspace_root.to_path_buf(), config); |
@@ -67,7 +71,7 @@ impl BuildDataCollector { | |||
67 | pub fn collect(&mut self, progress: &dyn Fn(String)) -> Result<BuildDataResult> { | 71 | pub fn collect(&mut self, progress: &dyn Fn(String)) -> Result<BuildDataResult> { |
68 | let mut res = BuildDataResult::default(); | 72 | let mut res = BuildDataResult::default(); |
69 | for (path, config) in self.configs.iter() { | 73 | for (path, config) in self.configs.iter() { |
70 | res.data.insert( | 74 | res.per_workspace.insert( |
71 | path.clone(), | 75 | path.clone(), |
72 | collect_from_workspace( | 76 | collect_from_workspace( |
73 | &config.cargo_toml, | 77 | &config.cargo_toml, |
@@ -81,9 +85,28 @@ impl BuildDataCollector { | |||
81 | } | 85 | } |
82 | } | 86 | } |
83 | 87 | ||
88 | impl WorkspaceBuildData { | ||
89 | pub(crate) fn get(&self, package_id: &str) -> Option<&PackageBuildData> { | ||
90 | self.per_package.get(package_id) | ||
91 | } | ||
92 | } | ||
93 | |||
84 | impl BuildDataResult { | 94 | impl BuildDataResult { |
85 | pub(crate) fn get(&self, root: &AbsPath) -> Option<&BuildDataMap> { | 95 | pub(crate) fn get(&self, workspace_root: &AbsPath) -> Option<&WorkspaceBuildData> { |
86 | self.data.get(&root.to_path_buf()) | 96 | self.per_workspace.get(workspace_root) |
97 | } | ||
98 | pub fn error(&self) -> Option<String> { | ||
99 | let mut buf = String::new(); | ||
100 | for (_workspace_root, build_data) in &self.per_workspace { | ||
101 | if let Some(err) = &build_data.error { | ||
102 | format_to!(buf, "cargo check failed:\n{}", err); | ||
103 | } | ||
104 | } | ||
105 | if buf.is_empty() { | ||
106 | return None; | ||
107 | } | ||
108 | |||
109 | Some(buf) | ||
87 | } | 110 | } |
88 | } | 111 | } |
89 | 112 | ||
@@ -102,7 +125,7 @@ fn collect_from_workspace( | |||
102 | cargo_features: &CargoConfig, | 125 | cargo_features: &CargoConfig, |
103 | packages: &Vec<cargo_metadata::Package>, | 126 | packages: &Vec<cargo_metadata::Package>, |
104 | progress: &dyn Fn(String), | 127 | progress: &dyn Fn(String), |
105 | ) -> Result<BuildDataMap> { | 128 | ) -> Result<WorkspaceBuildData> { |
106 | let mut cmd = Command::new(toolchain::cargo()); | 129 | let mut cmd = Command::new(toolchain::cargo()); |
107 | cmd.args(&["check", "--workspace", "--message-format=json", "--manifest-path"]) | 130 | cmd.args(&["check", "--workspace", "--message-format=json", "--manifest-path"]) |
108 | .arg(cargo_toml.as_ref()); | 131 | .arg(cargo_toml.as_ref()); |
@@ -130,13 +153,13 @@ fn collect_from_workspace( | |||
130 | } | 153 | } |
131 | } | 154 | } |
132 | 155 | ||
133 | cmd.stdout(Stdio::piped()).stderr(Stdio::null()).stdin(Stdio::null()); | 156 | cmd.stdout(Stdio::piped()).stderr(Stdio::piped()).stdin(Stdio::null()); |
134 | 157 | ||
135 | let mut child = cmd.spawn().map(JodChild)?; | 158 | let mut child = cmd.spawn().map(JodChild)?; |
136 | let child_stdout = child.stdout.take().unwrap(); | 159 | let child_stdout = child.stdout.take().unwrap(); |
137 | let stdout = BufReader::new(child_stdout); | 160 | let stdout = BufReader::new(child_stdout); |
138 | 161 | ||
139 | let mut res = BuildDataMap::default(); | 162 | let mut res = WorkspaceBuildData::default(); |
140 | for message in cargo_metadata::Message::parse_stream(stdout).flatten() { | 163 | for message in cargo_metadata::Message::parse_stream(stdout).flatten() { |
141 | match message { | 164 | match message { |
142 | Message::BuildScriptExecuted(BuildScript { | 165 | Message::BuildScriptExecuted(BuildScript { |
@@ -154,16 +177,17 @@ fn collect_from_workspace( | |||
154 | } | 177 | } |
155 | acc | 178 | acc |
156 | }; | 179 | }; |
157 | let res = res.entry(package_id.repr.clone()).or_default(); | 180 | let package_build_data = |
181 | res.per_package.entry(package_id.repr.clone()).or_default(); | ||
158 | // cargo_metadata crate returns default (empty) path for | 182 | // cargo_metadata crate returns default (empty) path for |
159 | // older cargos, which is not absolute, so work around that. | 183 | // older cargos, which is not absolute, so work around that. |
160 | if !out_dir.as_str().is_empty() { | 184 | if !out_dir.as_str().is_empty() { |
161 | let out_dir = AbsPathBuf::assert(PathBuf::from(out_dir.into_os_string())); | 185 | let out_dir = AbsPathBuf::assert(PathBuf::from(out_dir.into_os_string())); |
162 | res.out_dir = Some(out_dir); | 186 | package_build_data.out_dir = Some(out_dir); |
163 | res.cfgs = cfgs; | 187 | package_build_data.cfgs = cfgs; |
164 | } | 188 | } |
165 | 189 | ||
166 | res.envs = env; | 190 | package_build_data.envs = env; |
167 | } | 191 | } |
168 | Message::CompilerArtifact(message) => { | 192 | Message::CompilerArtifact(message) => { |
169 | progress(format!("metadata {}", message.target.name)); | 193 | progress(format!("metadata {}", message.target.name)); |
@@ -173,8 +197,9 @@ fn collect_from_workspace( | |||
173 | // Skip rmeta file | 197 | // Skip rmeta file |
174 | if let Some(filename) = message.filenames.iter().find(|name| is_dylib(name)) { | 198 | if let Some(filename) = message.filenames.iter().find(|name| is_dylib(name)) { |
175 | let filename = AbsPathBuf::assert(PathBuf::from(&filename)); | 199 | let filename = AbsPathBuf::assert(PathBuf::from(&filename)); |
176 | let res = res.entry(package_id.repr.clone()).or_default(); | 200 | let package_build_data = |
177 | res.proc_macro_dylib_path = Some(filename); | 201 | res.per_package.entry(package_id.repr.clone()).or_default(); |
202 | package_build_data.proc_macro_dylib_path = Some(filename); | ||
178 | } | 203 | } |
179 | } | 204 | } |
180 | } | 205 | } |
@@ -188,16 +213,25 @@ fn collect_from_workspace( | |||
188 | } | 213 | } |
189 | 214 | ||
190 | for package in packages { | 215 | for package in packages { |
191 | let build_data = res.entry(package.id.repr.clone()).or_default(); | 216 | let package_build_data = res.per_package.entry(package.id.repr.clone()).or_default(); |
192 | inject_cargo_env(package, build_data); | 217 | inject_cargo_env(package, package_build_data); |
193 | if let Some(out_dir) = &build_data.out_dir { | 218 | if let Some(out_dir) = &package_build_data.out_dir { |
194 | // NOTE: cargo and rustc seem to hide non-UTF-8 strings from env! and option_env!() | 219 | // NOTE: cargo and rustc seem to hide non-UTF-8 strings from env! and option_env!() |
195 | if let Some(out_dir) = out_dir.to_str().map(|s| s.to_owned()) { | 220 | if let Some(out_dir) = out_dir.to_str().map(|s| s.to_owned()) { |
196 | build_data.envs.push(("OUT_DIR".to_string(), out_dir)); | 221 | package_build_data.envs.push(("OUT_DIR".to_string(), out_dir)); |
197 | } | 222 | } |
198 | } | 223 | } |
199 | } | 224 | } |
200 | 225 | ||
226 | let output = child.into_inner().wait_with_output()?; | ||
227 | if !output.status.success() { | ||
228 | let mut stderr = String::from_utf8(output.stderr).unwrap_or_default(); | ||
229 | if stderr.is_empty() { | ||
230 | stderr = "cargo check failed".to_string(); | ||
231 | } | ||
232 | res.error = Some(stderr) | ||
233 | } | ||
234 | |||
201 | Ok(res) | 235 | Ok(res) |
202 | } | 236 | } |
203 | 237 | ||
@@ -212,7 +246,7 @@ fn is_dylib(path: &Utf8Path) -> bool { | |||
212 | /// Recreates the compile-time environment variables that Cargo sets. | 246 | /// Recreates the compile-time environment variables that Cargo sets. |
213 | /// | 247 | /// |
214 | /// Should be synced with <https://doc.rust-lang.org/cargo/reference/environment-variables.html#environment-variables-cargo-sets-for-crates> | 248 | /// Should be synced with <https://doc.rust-lang.org/cargo/reference/environment-variables.html#environment-variables-cargo-sets-for-crates> |
215 | fn inject_cargo_env(package: &cargo_metadata::Package, build_data: &mut BuildData) { | 249 | fn inject_cargo_env(package: &cargo_metadata::Package, build_data: &mut PackageBuildData) { |
216 | let env = &mut build_data.envs; | 250 | let env = &mut build_data.envs; |
217 | 251 | ||
218 | // FIXME: Missing variables: | 252 | // FIXME: Missing variables: |
diff --git a/crates/project_model/src/workspace.rs b/crates/project_model/src/workspace.rs index 1b53fcc30..2fcd0f8fa 100644 --- a/crates/project_model/src/workspace.rs +++ b/crates/project_model/src/workspace.rs | |||
@@ -12,7 +12,7 @@ use proc_macro_api::ProcMacroClient; | |||
12 | use rustc_hash::{FxHashMap, FxHashSet}; | 12 | use rustc_hash::{FxHashMap, FxHashSet}; |
13 | 13 | ||
14 | use crate::{ | 14 | use crate::{ |
15 | build_data::{BuildData, BuildDataMap, BuildDataResult}, | 15 | build_data::{BuildDataResult, PackageBuildData, WorkspaceBuildData}, |
16 | cargo_workspace, | 16 | cargo_workspace, |
17 | cfg_flag::CfgFlag, | 17 | cfg_flag::CfgFlag, |
18 | rustc_cfg, | 18 | rustc_cfg, |
@@ -354,10 +354,10 @@ fn cargo_to_crate_graph( | |||
354 | proc_macro_loader: &dyn Fn(&Path) -> Vec<ProcMacro>, | 354 | proc_macro_loader: &dyn Fn(&Path) -> Vec<ProcMacro>, |
355 | load: &mut dyn FnMut(&AbsPath) -> Option<FileId>, | 355 | load: &mut dyn FnMut(&AbsPath) -> Option<FileId>, |
356 | cargo: &CargoWorkspace, | 356 | cargo: &CargoWorkspace, |
357 | build_data_map: Option<&BuildDataMap>, | 357 | build_data_map: Option<&WorkspaceBuildData>, |
358 | sysroot: &Sysroot, | 358 | sysroot: &Sysroot, |
359 | rustc: &Option<CargoWorkspace>, | 359 | rustc: &Option<CargoWorkspace>, |
360 | rustc_build_data_map: Option<&BuildDataMap>, | 360 | rustc_build_data_map: Option<&WorkspaceBuildData>, |
361 | ) -> CrateGraph { | 361 | ) -> CrateGraph { |
362 | let _p = profile::span("cargo_to_crate_graph"); | 362 | let _p = profile::span("cargo_to_crate_graph"); |
363 | let mut crate_graph = CrateGraph::default(); | 363 | let mut crate_graph = CrateGraph::default(); |
@@ -464,7 +464,7 @@ fn handle_rustc_crates( | |||
464 | rustc_workspace: &CargoWorkspace, | 464 | rustc_workspace: &CargoWorkspace, |
465 | load: &mut dyn FnMut(&AbsPath) -> Option<FileId>, | 465 | load: &mut dyn FnMut(&AbsPath) -> Option<FileId>, |
466 | crate_graph: &mut CrateGraph, | 466 | crate_graph: &mut CrateGraph, |
467 | rustc_build_data_map: Option<&FxHashMap<String, BuildData>>, | 467 | rustc_build_data_map: Option<&WorkspaceBuildData>, |
468 | cfg_options: &CfgOptions, | 468 | cfg_options: &CfgOptions, |
469 | proc_macro_loader: &dyn Fn(&Path) -> Vec<ProcMacro>, | 469 | proc_macro_loader: &dyn Fn(&Path) -> Vec<ProcMacro>, |
470 | pkg_to_lib_crate: &mut FxHashMap<la_arena::Idx<crate::PackageData>, CrateId>, | 470 | pkg_to_lib_crate: &mut FxHashMap<la_arena::Idx<crate::PackageData>, CrateId>, |
@@ -555,7 +555,7 @@ fn handle_rustc_crates( | |||
555 | fn add_target_crate_root( | 555 | fn add_target_crate_root( |
556 | crate_graph: &mut CrateGraph, | 556 | crate_graph: &mut CrateGraph, |
557 | pkg: &cargo_workspace::PackageData, | 557 | pkg: &cargo_workspace::PackageData, |
558 | build_data: Option<&BuildData>, | 558 | build_data: Option<&PackageBuildData>, |
559 | cfg_options: &CfgOptions, | 559 | cfg_options: &CfgOptions, |
560 | proc_macro_loader: &dyn Fn(&Path) -> Vec<ProcMacro>, | 560 | proc_macro_loader: &dyn Fn(&Path) -> Vec<ProcMacro>, |
561 | file_id: FileId, | 561 | file_id: FileId, |
diff --git a/crates/rust-analyzer/src/reload.rs b/crates/rust-analyzer/src/reload.rs index 301c7003b..d0cc1b61a 100644 --- a/crates/rust-analyzer/src/reload.rs +++ b/crates/rust-analyzer/src/reload.rs | |||
@@ -109,6 +109,11 @@ impl GlobalState { | |||
109 | quiescent: self.is_quiescent(), | 109 | quiescent: self.is_quiescent(), |
110 | message: None, | 110 | message: None, |
111 | }; | 111 | }; |
112 | |||
113 | if let Some(error) = self.build_data_error() { | ||
114 | status.health = lsp_ext::Health::Warning; | ||
115 | status.message = Some(error) | ||
116 | } | ||
112 | if !self.config.cargo_autoreload() | 117 | if !self.config.cargo_autoreload() |
113 | && self.is_quiescent() | 118 | && self.is_quiescent() |
114 | && self.fetch_workspaces_queue.op_requested() | 119 | && self.fetch_workspaces_queue.op_requested() |
@@ -116,9 +121,10 @@ impl GlobalState { | |||
116 | status.health = lsp_ext::Health::Warning; | 121 | status.health = lsp_ext::Health::Warning; |
117 | status.message = Some("Workspace reload required".to_string()) | 122 | status.message = Some("Workspace reload required".to_string()) |
118 | } | 123 | } |
119 | if let Some(error) = self.loading_error() { | 124 | |
125 | if let Some(error) = self.fetch_workspace_error() { | ||
120 | status.health = lsp_ext::Health::Error; | 126 | status.health = lsp_ext::Health::Error; |
121 | status.message = Some(format!("Workspace reload failed: {}", error)) | 127 | status.message = Some(error) |
122 | } | 128 | } |
123 | 129 | ||
124 | if self.last_reported_status.as_ref() != Some(&status) { | 130 | if self.last_reported_status.as_ref() != Some(&status) { |
@@ -217,7 +223,7 @@ impl GlobalState { | |||
217 | let _p = profile::span("GlobalState::switch_workspaces"); | 223 | let _p = profile::span("GlobalState::switch_workspaces"); |
218 | log::info!("will switch workspaces"); | 224 | log::info!("will switch workspaces"); |
219 | 225 | ||
220 | if let Some(error_message) = self.loading_error() { | 226 | if let Some(error_message) = self.fetch_workspace_error() { |
221 | log::error!("failed to switch workspaces: {}", error_message); | 227 | log::error!("failed to switch workspaces: {}", error_message); |
222 | self.show_message(lsp_types::MessageType::Error, error_message); | 228 | self.show_message(lsp_types::MessageType::Error, error_message); |
223 | if !self.workspaces.is_empty() { | 229 | if !self.workspaces.is_empty() { |
@@ -225,6 +231,11 @@ impl GlobalState { | |||
225 | } | 231 | } |
226 | } | 232 | } |
227 | 233 | ||
234 | if let Some(error_message) = self.build_data_error() { | ||
235 | log::error!("failed to switch build data: {}", error_message); | ||
236 | self.show_message(lsp_types::MessageType::Error, error_message); | ||
237 | } | ||
238 | |||
228 | let workspaces = self | 239 | let workspaces = self |
229 | .fetch_workspaces_queue | 240 | .fetch_workspaces_queue |
230 | .last_op_result() | 241 | .last_op_result() |
@@ -343,22 +354,30 @@ impl GlobalState { | |||
343 | log::info!("did switch workspaces"); | 354 | log::info!("did switch workspaces"); |
344 | } | 355 | } |
345 | 356 | ||
346 | fn loading_error(&self) -> Option<String> { | 357 | fn fetch_workspace_error(&self) -> Option<String> { |
347 | let mut message = None; | 358 | let mut buf = String::new(); |
348 | 359 | ||
349 | for ws in self.fetch_workspaces_queue.last_op_result() { | 360 | for ws in self.fetch_workspaces_queue.last_op_result() { |
350 | if let Err(err) = ws { | 361 | if let Err(err) = ws { |
351 | let message = message.get_or_insert_with(String::new); | 362 | stdx::format_to!(buf, "rust-analyzer failed to load workspace: {:#}\n", err); |
352 | stdx::format_to!(message, "rust-analyzer failed to load workspace: {:#}\n", err); | ||
353 | } | 363 | } |
354 | } | 364 | } |
355 | 365 | ||
356 | if let Some(Err(err)) = self.fetch_build_data_queue.last_op_result() { | 366 | if buf.is_empty() { |
357 | let message = message.get_or_insert_with(String::new); | 367 | return None; |
358 | stdx::format_to!(message, "rust-analyzer failed to fetch build data: {:#}\n", err); | ||
359 | } | 368 | } |
360 | 369 | ||
361 | message | 370 | Some(buf) |
371 | } | ||
372 | |||
373 | fn build_data_error(&self) -> Option<String> { | ||
374 | match self.fetch_build_data_queue.last_op_result() { | ||
375 | Some(Err(err)) => { | ||
376 | Some(format!("rust-analyzer failed to fetch build data: {:#}\n", err)) | ||
377 | } | ||
378 | Some(Ok(data)) => data.error(), | ||
379 | None => None, | ||
380 | } | ||
362 | } | 381 | } |
363 | 382 | ||
364 | fn reload_flycheck(&mut self) { | 383 | fn reload_flycheck(&mut self) { |
diff --git a/crates/stdx/src/lib.rs b/crates/stdx/src/lib.rs index d26be4853..b0a18d58d 100644 --- a/crates/stdx/src/lib.rs +++ b/crates/stdx/src/lib.rs | |||
@@ -178,6 +178,7 @@ where | |||
178 | start..start + len | 178 | start..start + len |
179 | } | 179 | } |
180 | 180 | ||
181 | #[repr(transparent)] | ||
181 | pub struct JodChild(pub process::Child); | 182 | pub struct JodChild(pub process::Child); |
182 | 183 | ||
183 | impl ops::Deref for JodChild { | 184 | impl ops::Deref for JodChild { |
@@ -200,6 +201,13 @@ impl Drop for JodChild { | |||
200 | } | 201 | } |
201 | } | 202 | } |
202 | 203 | ||
204 | impl JodChild { | ||
205 | pub fn into_inner(self) -> process::Child { | ||
206 | // SAFETY: repr transparent | ||
207 | unsafe { std::mem::transmute::<JodChild, process::Child>(self) } | ||
208 | } | ||
209 | } | ||
210 | |||
203 | #[cfg(test)] | 211 | #[cfg(test)] |
204 | mod tests { | 212 | mod tests { |
205 | use super::*; | 213 | use super::*; |