Skip to content

Commit 9318c64

Browse files
committed
internal: make it easier to isolate IO
1 parent a2f83c9 commit 9318c64

File tree

8 files changed

+58
-35
lines changed

8 files changed

+58
-35
lines changed

crates/paths/src/lib.rs

Lines changed: 35 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
use std::{
44
borrow::Borrow,
55
convert::{TryFrom, TryInto},
6+
ffi::OsStr,
67
ops,
78
path::{Component, Path, PathBuf},
89
};
@@ -97,13 +98,6 @@ impl AbsPathBuf {
9798
#[repr(transparent)]
9899
pub struct AbsPath(Path);
99100

100-
impl ops::Deref for AbsPath {
101-
type Target = Path;
102-
fn deref(&self) -> &Path {
103-
&self.0
104-
}
105-
}
106-
107101
impl AsRef<Path> for AbsPath {
108102
fn as_ref(&self) -> &Path {
109103
&self.0
@@ -168,6 +162,40 @@ impl AbsPath {
168162
pub fn strip_prefix(&self, base: &AbsPath) -> Option<&RelPath> {
169163
self.0.strip_prefix(base).ok().map(RelPath::new_unchecked)
170164
}
165+
pub fn starts_with(&self, base: &AbsPath) -> bool {
166+
self.0.starts_with(&base.0)
167+
}
168+
169+
// region:delegate-methods
170+
171+
// Note that we deliberately don't implement `Deref<Target = Path>` here.
172+
//
173+
// The problem with `Path` is that it directly exposes convenience IO-ing
174+
// methods. For example, `Path::exists` delegates to `fs::metadata`.
175+
//
176+
// For `AbsPath`, we want to make sure that this is a POD type, and that all
177+
// IO goes via `fs`. That way, it becomes easier to mock IO when we need it.
178+
179+
pub fn file_name(&self) -> Option<&OsStr> {
180+
self.0.file_name()
181+
}
182+
pub fn extension(&self) -> Option<&OsStr> {
183+
self.0.extension()
184+
}
185+
pub fn file_stem(&self) -> Option<&OsStr> {
186+
self.0.file_stem()
187+
}
188+
pub fn as_os_str(&self) -> &OsStr {
189+
self.0.as_os_str()
190+
}
191+
pub fn display(&self) -> std::path::Display<'_> {
192+
self.0.display()
193+
}
194+
#[deprecated(note = "use std::fs::metadata().is_ok() instead")]
195+
pub fn exists(&self) -> bool {
196+
self.0.exists()
197+
}
198+
// endregion:delegate-methods
171199
}
172200

173201
/// Wrapper around a relative [`PathBuf`].
@@ -224,13 +252,6 @@ impl RelPathBuf {
224252
#[repr(transparent)]
225253
pub struct RelPath(Path);
226254

227-
impl ops::Deref for RelPath {
228-
type Target = Path;
229-
fn deref(&self) -> &Path {
230-
&self.0
231-
}
232-
}
233-
234255
impl AsRef<Path> for RelPath {
235256
fn as_ref(&self) -> &Path {
236257
&self.0

crates/proc_macro_api/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@ impl ProcMacroClient {
8686
Ok(ProcMacroClient { process: Arc::new(Mutex::new(process)) })
8787
}
8888

89+
// TODO: use paths::AbsPath here
8990
pub fn by_dylib_path(&self, dylib_path: &Path) -> Vec<ProcMacro> {
9091
let _p = profile::span("ProcMacroClient::by_dylib_path");
9192
match version::read_dylib_info(dylib_path) {

crates/project_model/src/build_data.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -258,7 +258,7 @@ impl WorkspaceBuildData {
258258
inject_cargo_env(package, package_build_data);
259259
if let Some(out_dir) = &package_build_data.out_dir {
260260
// NOTE: cargo and rustc seem to hide non-UTF-8 strings from env! and option_env!()
261-
if let Some(out_dir) = out_dir.to_str().map(|s| s.to_owned()) {
261+
if let Some(out_dir) = out_dir.as_os_str().to_str().map(|s| s.to_owned()) {
262262
package_build_data.envs.push(("OUT_DIR".to_string(), out_dir));
263263
}
264264
}

crates/project_model/src/cargo_workspace.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -273,11 +273,11 @@ impl CargoWorkspace {
273273
.parent()
274274
.map(|p| p.to_path_buf())
275275
.or(cwd)
276-
.map(|dir| dir.to_string_lossy().to_string())
277-
.unwrap_or_else(|| "<failed to get path>".into());
276+
.map(|dir| format!(" in `{}`", dir.display()))
277+
.unwrap_or_default();
278278

279279
format!(
280-
"Failed to run `cargo metadata --manifest-path {}` in `{}`",
280+
"Failed to run `cargo metadata --manifest-path {}`{}",
281281
cargo_toml.display(),
282282
workdir
283283
)

crates/project_model/src/lib.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ mod rustc_cfg;
2424
mod build_data;
2525

2626
use std::{
27-
fs::{read_dir, ReadDir},
27+
fs::{self, read_dir, ReadDir},
2828
io,
2929
process::Command,
3030
};
@@ -54,10 +54,10 @@ pub enum ProjectManifest {
5454

5555
impl ProjectManifest {
5656
pub fn from_manifest_file(path: AbsPathBuf) -> Result<ProjectManifest> {
57-
if path.ends_with("rust-project.json") {
57+
if path.file_name().unwrap_or_default() == "rust-project.json" {
5858
return Ok(ProjectManifest::ProjectJson(path));
5959
}
60-
if path.ends_with("Cargo.toml") {
60+
if path.file_name().unwrap_or_default() == "Cargo.toml" {
6161
return Ok(ProjectManifest::CargoToml(path));
6262
}
6363
bail!("project root must point to Cargo.toml or rust-project.json: {}", path.display())
@@ -91,15 +91,15 @@ impl ProjectManifest {
9191
}
9292

9393
fn find_in_parent_dirs(path: &AbsPath, target_file_name: &str) -> Option<AbsPathBuf> {
94-
if path.ends_with(target_file_name) {
94+
if path.file_name().unwrap_or_default() == target_file_name {
9595
return Some(path.to_path_buf());
9696
}
9797

9898
let mut curr = Some(path);
9999

100100
while let Some(path) = curr {
101101
let candidate = path.join(target_file_name);
102-
if candidate.exists() {
102+
if fs::metadata(&candidate).is_ok() {
103103
return Some(candidate);
104104
}
105105
curr = path.parent();

crates/project_model/src/sysroot.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
//! but we can't process `.rlib` and need source code instead. The source code
55
//! is typically installed with `rustup component add rust-src` command.
66
7-
use std::{convert::TryFrom, env, ops, path::PathBuf, process::Command};
7+
use std::{convert::TryFrom, env, fs, ops, path::PathBuf, process::Command};
88

99
use anyhow::{format_err, Result};
1010
use la_arena::{Arena, Idx};
@@ -73,7 +73,7 @@ impl Sysroot {
7373
let root = [format!("{}/src/lib.rs", path), format!("lib{}/lib.rs", path)]
7474
.iter()
7575
.map(|it| sysroot_src_dir.join(it))
76-
.find(|it| it.exists());
76+
.find(|it| fs::metadata(it).is_ok());
7777

7878
if let Some(root) = root {
7979
sysroot.crates.alloc(SysrootCrateData {
@@ -142,7 +142,7 @@ fn discover_sysroot_src_dir(
142142
let path = AbsPathBuf::try_from(path.as_str())
143143
.map_err(|path| format_err!("RUST_SRC_PATH must be absolute: {}", path.display()))?;
144144
let core = path.join("core");
145-
if core.exists() {
145+
if fs::metadata(&core).is_ok() {
146146
log::debug!("Discovered sysroot by RUST_SRC_PATH: {}", path.display());
147147
return Ok(path);
148148
}
@@ -171,7 +171,7 @@ try installing the Rust source the same way you installed rustc",
171171
fn get_rustc_src(sysroot_path: &AbsPath) -> Option<AbsPathBuf> {
172172
let rustc_src = sysroot_path.join("lib/rustlib/rustc-src/rust/compiler/rustc/Cargo.toml");
173173
log::debug!("Checking for rustc source code: {}", rustc_src.display());
174-
if rustc_src.exists() {
174+
if fs::metadata(&rustc_src).is_ok() {
175175
Some(rustc_src)
176176
} else {
177177
None
@@ -182,7 +182,7 @@ fn get_rust_src(sysroot_path: &AbsPath) -> Option<AbsPathBuf> {
182182
// Try the new path first since the old one still exists.
183183
let rust_src = sysroot_path.join("lib/rustlib/src/rust");
184184
log::debug!("Checking sysroot (looking for `library` and `src` dirs): {}", rust_src.display());
185-
["library", "src"].iter().map(|it| rust_src.join(it)).find(|it| it.exists())
185+
["library", "src"].iter().map(|it| rust_src.join(it)).find(|it| fs::metadata(it).is_ok())
186186
}
187187

188188
impl SysrootCrateData {

crates/project_model/src/workspace.rs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
//! metadata` or `rust-project.json`) into representation stored in the salsa
33
//! database -- `CrateGraph`.
44
5-
use std::{collections::VecDeque, fmt, fs, path::Path, process::Command};
5+
use std::{collections::VecDeque, fmt, fs, process::Command};
66

77
use anyhow::{format_err, Context, Result};
88
use base_db::{CrateDisplayName, CrateGraph, CrateId, CrateName, Edition, Env, FileId, ProcMacro};
@@ -311,8 +311,8 @@ impl ProjectWorkspace {
311311
load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
312312
) -> CrateGraph {
313313
let _p = profile::span("ProjectWorkspace::to_crate_graph");
314-
let proc_macro_loader = |path: &Path| match proc_macro_client {
315-
Some(client) => client.by_dylib_path(path),
314+
let proc_macro_loader = |path: &AbsPath| match proc_macro_client {
315+
Some(client) => client.by_dylib_path(path.as_ref()), // TODO
316316
None => Vec::new(),
317317
};
318318

@@ -364,7 +364,7 @@ impl ProjectWorkspace {
364364

365365
fn project_json_to_crate_graph(
366366
rustc_cfg: Vec<CfgFlag>,
367-
proc_macro_loader: &dyn Fn(&Path) -> Vec<ProcMacro>,
367+
proc_macro_loader: &dyn Fn(&AbsPath) -> Vec<ProcMacro>,
368368
load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
369369
project: &ProjectJson,
370370
sysroot: &Option<Sysroot>,
@@ -431,7 +431,7 @@ fn project_json_to_crate_graph(
431431
fn cargo_to_crate_graph(
432432
rustc_cfg: Vec<CfgFlag>,
433433
override_cfg: &CfgOverrides,
434-
proc_macro_loader: &dyn Fn(&Path) -> Vec<ProcMacro>,
434+
proc_macro_loader: &dyn Fn(&AbsPath) -> Vec<ProcMacro>,
435435
load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
436436
cargo: &CargoWorkspace,
437437
build_data_map: Option<&WorkspaceBuildData>,
@@ -616,7 +616,7 @@ fn handle_rustc_crates(
616616
crate_graph: &mut CrateGraph,
617617
rustc_build_data_map: Option<&WorkspaceBuildData>,
618618
cfg_options: &CfgOptions,
619-
proc_macro_loader: &dyn Fn(&Path) -> Vec<ProcMacro>,
619+
proc_macro_loader: &dyn Fn(&AbsPath) -> Vec<ProcMacro>,
620620
pkg_to_lib_crate: &mut FxHashMap<la_arena::Idx<crate::PackageData>, CrateId>,
621621
public_deps: &[(CrateName, CrateId)],
622622
cargo: &CargoWorkspace,
@@ -708,7 +708,7 @@ fn add_target_crate_root(
708708
pkg: &cargo_workspace::PackageData,
709709
build_data: Option<&PackageBuildData>,
710710
cfg_options: &CfgOptions,
711-
proc_macro_loader: &dyn Fn(&Path) -> Vec<ProcMacro>,
711+
proc_macro_loader: &dyn Fn(&AbsPath) -> Vec<ProcMacro>,
712712
file_id: FileId,
713713
cargo_name: &str,
714714
) -> CrateId {

crates/vfs/src/vfs_path.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,7 @@ impl VfsPath {
121121
#[cfg(windows)]
122122
{
123123
use windows_paths::Encode;
124+
let path: &std::path::Path = path.as_ref();
124125
let components = path.components();
125126
let mut add_sep = false;
126127
for component in components {

0 commit comments

Comments
 (0)