Compare commits

...
Sign in to create a new pull request.

1 commit

Author SHA1 Message Date
Jordan Pittman
9644c1844a Use custom implementation of canonicalize for WASI 2025-09-25 16:52:21 -04:00
11 changed files with 74 additions and 11 deletions

9
Cargo.lock generated
View file

@ -198,6 +198,13 @@ version = "2.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e8c02a5121d4ea3eb16a80748c74f5549a5665e4c21333c6098f283870fbdea6"
[[package]]
name = "fsops"
version = "0.1.0"
dependencies = [
"dunce",
]
[[package]]
name = "globset"
version = "0.4.16"
@ -230,6 +237,7 @@ dependencies = [
"crossbeam-channel",
"crossbeam-deque",
"dunce",
"fsops",
"globset",
"log",
"memchr",
@ -598,6 +606,7 @@ dependencies = [
"crossbeam",
"dunce",
"fast-glob",
"fsops",
"globwalk",
"ignore 0.4.23",
"log",

11
crates/fsops/Cargo.toml Normal file
View file

@ -0,0 +1,11 @@
[package]
name = "fsops"
version = "0.1.0"
edition = "2021"
[lib]
name = "fsops"
bench = false
[dependencies]
dunce = "1.0.5"

41
crates/fsops/src/lib.rs Normal file
View file

@ -0,0 +1,41 @@
use std::io;
use std::path::{Path, PathBuf};
/// https://github.com/oxc-project/oxc-resolver/blob/42a1e3eb50e9a1365c422c41d51f287fe5fb8244/src/file_system.rs#L93-L122
pub fn canonicalize<P: AsRef<Path>>(path: P) -> io::Result<PathBuf> {
#[cfg(not(target_os = "wasi"))]
{
dunce::canonicalize(path)
}
#[cfg(target_os = "wasi")]
{
canonicalize_wasi(path)
}
}
#[cfg(target_os = "wasi")]
fn canonicalize_wasi<P: AsRef<Path>>(path: P) -> io::Result<PathBuf> {
use std::fs;
let path = path.as_ref();
let meta = fs::symlink_metadata(path)?;
if meta.file_type().is_symlink() {
let link = fs::read_link(path)?;
let mut path_buf = path.to_path_buf();
path_buf.pop();
for segment in link.iter() {
match segment.to_str() {
Some("..") => {
path_buf.pop();
}
Some(".") | None => {}
Some(seg) => {
// Need to trim the extra \0 introduces by rust std rust-lang/rust#123727
path_buf.push(seg.trim_end_matches('\0'));
}
}
}
Ok(path_buf)
} else {
Ok(path.to_path_buf())
}
}

View file

@ -26,6 +26,7 @@ memchr = "2.6.3"
same-file = "1.0.6"
walkdir = "2.4.0"
dunce = "1.0.5"
fsops = { path = "../fsops" }
[dependencies.regex-automata]
version = "0.4.0"

View file

@ -176,8 +176,8 @@ impl Ignore {
if !self.is_root() {
panic!("Ignore::add_parents called on non-root matcher");
}
// CHANGED: Use `dunce::canonicalize` as we use it everywhere else.
let absolute_base = match dunce::canonicalize(path.as_ref()) {
// CHANGED: Use `fsops::canonicalize` as we use it everywhere else.
let absolute_base = match fsops::canonicalize(path.as_ref()) {
Ok(path) => Arc::new(path),
Err(_) => {
// There's not much we can do here, so just return our

View file

@ -19,6 +19,7 @@ fast-glob = "0.4.3"
classification-macros = { path = "../classification-macros" }
ignore = { path = "../ignore" }
regex = "1.11.1"
fsops = { path = "../fsops" }
[dev-dependencies]
tempfile = "3.13.0"

View file

@ -23,7 +23,7 @@ pub fn hoist_static_glob_parts(entries: &Vec<GlobEntry>, emit_parent_glob: bool)
None => base,
};
let base = match dunce::canonicalize(&base) {
let base = match fsops::canonicalize(&base) {
Ok(base) => base,
Err(err) => {
event!(tracing::Level::ERROR, "Failed to resolve glob: {:?}", err);
@ -253,7 +253,7 @@ mod tests {
let optimized_sources = optimize_patterns(&sources);
let parent_dir =
format!("{}{}", dunce::canonicalize(base).unwrap().display(), "/").replace('\\', "/");
format!("{}{}", fsops::canonicalize(base).unwrap().display(), "/").replace('\\', "/");
// Remove the temporary directory from the base
optimized_sources

View file

@ -60,6 +60,6 @@ impl Path {
}
pub fn canonicalize(&self) -> io::Result<Self> {
Ok(dunce::canonicalize(&self.inner)?.into())
Ok(fsops::canonicalize(&self.inner)?.into())
}
}

View file

@ -63,7 +63,7 @@ fn init_tracing() {
.unwrap_or_else(|_| panic!("Failed to open {file_path}"));
let file_path = Path::new(&file_path);
let absolute_file_path = dunce::canonicalize(file_path)
let absolute_file_path = fsops::canonicalize(file_path)
.unwrap_or_else(|_| panic!("Failed to canonicalize {file_path:?}"));
eprintln!(
"{} Writing debug info to: {}\n",
@ -215,7 +215,7 @@ impl Scanner {
.into_iter()
.filter_map(|changed_content| match changed_content {
ChangedContent::File(file, extension) => {
let Ok(file) = dunce::canonicalize(file) else {
let Ok(file) = fsops::canonicalize(file) else {
return None;
};
Some(ChangedContent::File(file, extension))

View file

@ -102,7 +102,7 @@ impl PublicSourceEntry {
/// resolved path.
pub fn optimize(&mut self) {
// Resolve base path immediately
let Ok(base) = dunce::canonicalize(&self.base) else {
let Ok(base) = fsops::canonicalize(&self.base) else {
event!(Level::ERROR, "Failed to resolve base: {:?}", self.base);
return;
};
@ -116,7 +116,7 @@ impl PublicSourceEntry {
PathBuf::from(&self.base).join(&self.pattern)
};
match dunce::canonicalize(combined_path) {
match fsops::canonicalize(combined_path) {
Ok(resolved_path) if resolved_path.is_dir() => {
self.base = resolved_path.to_string_lossy().to_string();
self.pattern = "**/*".to_owned();
@ -144,7 +144,7 @@ impl PublicSourceEntry {
Some(static_part) => {
// TODO: If the base does not exist on disk, try removing the last slash and try
// again.
match dunce::canonicalize(base.join(static_part)) {
match fsops::canonicalize(base.join(static_part)) {
Ok(base) => base,
Err(err) => {
event!(tracing::Level::ERROR, "Failed to resolve glob: {:?}", err);

View file

@ -87,7 +87,7 @@ mod scanner {
let candidates = scanner.scan();
let base_dir =
format!("{}{}", dunce::canonicalize(&base).unwrap().display(), "/").replace('\\', "/");
format!("{}{}", fsops::canonicalize(&base).unwrap().display(), "/").replace('\\', "/");
// Get all scanned files as strings relative to the base directory
let mut files = scanner