mirror of
https://github.com/openai/codex.git
synced 2026-10-27 18:48:37 +00:00
refactor: make shared path and cache utilities wasm-safe
Move native-only path and Tokio dependencies behind cfg gates so shared crates can compile for wasm32. Replace the absolute-path implementation with a lexical normalization fallback on wasm, use a std mutex for the cache helper on wasm, and degrade git symlink creation to an unsupported-platform error instead of a compile-time failure.
This commit is contained in:
@@ -30,6 +30,8 @@ pub enum GitToolingError {
|
||||
PathPrefix(#[from] std::path::StripPrefixError),
|
||||
#[error(transparent)]
|
||||
Walkdir(#[from] WalkdirError),
|
||||
#[error("{0}")]
|
||||
UnsupportedPlatform(String),
|
||||
#[error(transparent)]
|
||||
Io(#[from] std::io::Error),
|
||||
}
|
||||
|
||||
@@ -34,4 +34,12 @@ pub fn create_symlink(
|
||||
}
|
||||
|
||||
#[cfg(not(any(unix, windows)))]
|
||||
compile_error!("codex-git symlink support is only implemented for Unix and Windows");
|
||||
pub fn create_symlink(
|
||||
_source: &Path,
|
||||
_link_target: &Path,
|
||||
_destination: &Path,
|
||||
) -> Result<(), GitToolingError> {
|
||||
Err(GitToolingError::UnsupportedPlatform(
|
||||
"symlink creation is unavailable on this target".to_string(),
|
||||
))
|
||||
}
|
||||
|
||||
@@ -10,7 +10,6 @@ workspace = true
|
||||
|
||||
[dependencies]
|
||||
dirs = { workspace = true }
|
||||
path-absolutize = { workspace = true }
|
||||
schemars = { workspace = true }
|
||||
serde = { workspace = true, features = ["derive"] }
|
||||
ts-rs = { workspace = true, features = [
|
||||
@@ -22,3 +21,6 @@ ts-rs = { workspace = true, features = [
|
||||
pretty_assertions = { workspace = true }
|
||||
serde_json = { workspace = true }
|
||||
tempfile = { workspace = true }
|
||||
|
||||
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
|
||||
path-absolutize = { workspace = true }
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
use dirs::home_dir;
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
use path_absolutize::Absolutize;
|
||||
use schemars::JsonSchema;
|
||||
use serde::Deserialize;
|
||||
@@ -6,6 +7,10 @@ use serde::Deserializer;
|
||||
use serde::Serialize;
|
||||
use serde::de::Error as SerdeError;
|
||||
use std::cell::RefCell;
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
use std::ffi::OsString;
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
use std::path::Component;
|
||||
use std::path::Display;
|
||||
use std::path::Path;
|
||||
use std::path::PathBuf;
|
||||
@@ -45,13 +50,24 @@ impl AbsolutePathBuf {
|
||||
base_path: B,
|
||||
) -> std::io::Result<Self> {
|
||||
let expanded = Self::maybe_expand_home_directory(path.as_ref());
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
let absolute_path = expanded.absolutize_from(base_path.as_ref())?;
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
let absolute_path =
|
||||
normalize_absolute_path(join_against_base(expanded, base_path.as_ref())?);
|
||||
Ok(Self(absolute_path.into_owned()))
|
||||
}
|
||||
|
||||
pub fn from_absolute_path<P: AsRef<Path>>(path: P) -> std::io::Result<Self> {
|
||||
let expanded = Self::maybe_expand_home_directory(path.as_ref());
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
let absolute_path = expanded.absolutize()?;
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
let absolute_path = normalize_absolute_path(if expanded.is_absolute() {
|
||||
expanded
|
||||
} else {
|
||||
join_against_base(expanded, &std::env::current_dir()?)?
|
||||
});
|
||||
Ok(Self(absolute_path.into_owned()))
|
||||
}
|
||||
|
||||
@@ -101,6 +117,56 @@ impl AbsolutePathBuf {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
fn join_against_base(path: PathBuf, base_path: &Path) -> std::io::Result<PathBuf> {
|
||||
if path.is_absolute() {
|
||||
return Ok(path);
|
||||
}
|
||||
if !base_path.is_absolute() {
|
||||
return Err(std::io::Error::new(
|
||||
std::io::ErrorKind::InvalidInput,
|
||||
"base path must be absolute",
|
||||
));
|
||||
}
|
||||
Ok(base_path.join(path))
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
fn normalize_absolute_path(path: PathBuf) -> std::borrow::Cow<'static, Path> {
|
||||
let mut prefix: Option<OsString> = None;
|
||||
let mut has_root = false;
|
||||
let mut parts: Vec<OsString> = Vec::new();
|
||||
|
||||
for component in path.components() {
|
||||
match component {
|
||||
Component::Prefix(value) => {
|
||||
prefix = Some(value.as_os_str().to_os_string());
|
||||
}
|
||||
Component::RootDir => {
|
||||
has_root = true;
|
||||
}
|
||||
Component::CurDir => {}
|
||||
Component::ParentDir => {
|
||||
let _ = parts.pop();
|
||||
}
|
||||
Component::Normal(part) => parts.push(part.to_os_string()),
|
||||
}
|
||||
}
|
||||
|
||||
let mut normalized = PathBuf::new();
|
||||
if let Some(prefix) = prefix {
|
||||
normalized.push(prefix);
|
||||
}
|
||||
if has_root {
|
||||
normalized.push(Path::new(std::path::MAIN_SEPARATOR_STR));
|
||||
}
|
||||
for part in parts {
|
||||
normalized.push(part);
|
||||
}
|
||||
|
||||
std::borrow::Cow::Owned(normalized)
|
||||
}
|
||||
|
||||
impl AsRef<Path> for AbsolutePathBuf {
|
||||
fn as_ref(&self) -> &Path {
|
||||
&self.0
|
||||
|
||||
Vendored
+3
-1
@@ -10,7 +10,9 @@ workspace = true
|
||||
[dependencies]
|
||||
lru = { workspace = true }
|
||||
sha1 = { workspace = true }
|
||||
tokio = { workspace = true, features = ["sync", "rt", "rt-multi-thread"] }
|
||||
|
||||
[dev-dependencies]
|
||||
tokio = { workspace = true, features = ["macros", "rt", "rt-multi-thread"] }
|
||||
|
||||
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
|
||||
tokio = { workspace = true, features = ["sync", "rt", "rt-multi-thread"] }
|
||||
|
||||
Vendored
+16
@@ -5,7 +5,13 @@ use std::num::NonZeroUsize;
|
||||
use lru::LruCache;
|
||||
use sha1::Digest;
|
||||
use sha1::Sha1;
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
use std::sync::Mutex;
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
use std::sync::MutexGuard;
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
use tokio::sync::Mutex;
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
use tokio::sync::MutexGuard;
|
||||
|
||||
/// A minimal LRU cache protected by a Tokio mutex.
|
||||
@@ -119,6 +125,7 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
fn lock_if_runtime<K, V>(m: &Mutex<LruCache<K, V>>) -> Option<MutexGuard<'_, LruCache<K, V>>>
|
||||
where
|
||||
K: Eq + Hash,
|
||||
@@ -127,6 +134,14 @@ where
|
||||
Some(tokio::task::block_in_place(|| m.blocking_lock()))
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
fn lock_if_runtime<K, V>(m: &Mutex<LruCache<K, V>>) -> Option<MutexGuard<'_, LruCache<K, V>>>
|
||||
where
|
||||
K: Eq + Hash,
|
||||
{
|
||||
m.lock().ok()
|
||||
}
|
||||
|
||||
/// Computes the SHA-1 digest of `bytes`.
|
||||
///
|
||||
/// Useful for content-based cache keys when you want to avoid staleness
|
||||
@@ -141,6 +156,7 @@ pub fn sha1_digest(bytes: &[u8]) -> [u8; 20] {
|
||||
out
|
||||
}
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::BlockingLruCache;
|
||||
|
||||
Reference in New Issue
Block a user