Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion crates/vfox/src/cli/env_keys.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,13 @@ pub struct EnvKeys {
impl EnvKeys {
pub async fn run(&self) -> VfoxResult<()> {
let vfox = Vfox::new();
let env_keys = vfox.env_keys(&self.sdk, &self.version).await?;
let env_keys = vfox
.env_keys(
&self.sdk,
&self.version,
serde_json::Value::Object(Default::default()),
)
.await?;
for env_key in env_keys {
println!("{}={}", env_key.key, env_key.value);
}
Expand Down
13 changes: 9 additions & 4 deletions crates/vfox/src/hooks/env_keys.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use mlua::prelude::LuaError;
use mlua::{FromLua, IntoLua, Lua, Value};
use mlua::{FromLua, IntoLua, Lua, LuaSerdeExt, Value};
use std::collections::BTreeMap;
use std::path::PathBuf;

Expand All @@ -14,16 +14,20 @@ pub struct EnvKey {
}

#[derive(Debug)]
pub struct EnvKeysContext {
pub struct EnvKeysContext<T: serde::Serialize> {
pub args: Vec<String>,
pub version: String,
pub path: PathBuf,
pub main: SdkInfo,
pub sdk_info: BTreeMap<String, SdkInfo>,
pub options: T,
}

impl Plugin {
pub async fn env_keys(&self, ctx: EnvKeysContext) -> Result<Vec<EnvKey>> {
pub async fn env_keys<T: serde::Serialize>(
&self,
ctx: EnvKeysContext<T>,
) -> Result<Vec<EnvKey>> {
Comment on lines +27 to +30
Copy link

Copilot AI Dec 19, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The generic parameter T should have a Clone bound in addition to serde::Serialize. This matches the constraint needed in the parent method and ensures the context can be cloned if needed by plugin implementations.

Copilot uses AI. Check for mistakes.
debug!("[vfox:{}] env_keys", &self.name);
let env_keys = self
.eval_async(chunk! {
Expand All @@ -36,13 +40,14 @@ impl Plugin {
}
}

impl IntoLua for EnvKeysContext {
impl<T: serde::Serialize> IntoLua for EnvKeysContext<T> {
fn into_lua(self, lua: &Lua) -> mlua::Result<Value> {
let table = lua.create_table()?;
table.set("version", self.version)?;
table.set("path", self.path.to_string_lossy().to_string())?;
table.set("sdkInfo", self.sdk_info)?;
table.set("main", self.main)?;
table.set("options", lua.to_value(&self.options)?)?;
Ok(Value::Table(table))
}
}
Expand Down
17 changes: 15 additions & 2 deletions crates/vfox/src/vfox.rs
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,12 @@ impl Vfox {
self.get_sdk(sdk)?.get_metadata()
}

pub async fn env_keys(&self, sdk: &str, version: &str) -> Result<Vec<EnvKey>> {
pub async fn env_keys<T: serde::Serialize>(
&self,
sdk: &str,
version: &str,
options: T,
) -> Result<Vec<EnvKey>> {
Comment on lines +200 to +205
Copy link

Copilot AI Dec 19, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The generic parameter T should have a Clone bound in addition to serde::Serialize. The options parameter is moved into EnvKeysContext on line 218, but if this method is called multiple times or if the SDK implementation needs to clone the options internally, this will fail to compile.

Copilot uses AI. Check for mistakes.
debug!("Getting env keys for {sdk} version {version}");
let sdk = self.get_sdk(sdk)?;
let sdk_info = sdk.sdk_info(
Expand All @@ -210,6 +215,7 @@ impl Vfox {
path: sdk_info.path.clone(),
sdk_info: BTreeMap::from([(sdk_info.name.clone(), sdk_info.clone())]),
main: sdk_info,
options,
};
sdk.env_keys(ctx).await
}
Expand Down Expand Up @@ -445,7 +451,14 @@ mod tests {
async fn test_env_keys() {
let vfox = Vfox::test();
// dummy plugin already exists in plugins/dummy, no need to install
let keys = vfox.env_keys("dummy", "1.0.0").await.unwrap();
let keys = vfox
.env_keys(
"dummy",
"1.0.0",
serde_json::Value::Object(Default::default()),
)
.await
.unwrap();
let output = format!("{keys:?}").replace(
&vfox.install_dir.to_string_lossy().to_string(),
"<INSTALL_DIR>",
Expand Down
15 changes: 12 additions & 3 deletions src/backend/vfox.rs
Original file line number Diff line number Diff line change
Expand Up @@ -245,12 +245,20 @@ impl VfoxBackend {
config: &Arc<Config>,
tv: &ToolVersion,
) -> eyre::Result<BTreeMap<String, String>> {
let key = tv.to_string();
let opts = tv.request.options();
let opts_hash = {
use std::hash::{Hash, Hasher};
let mut hasher = std::collections::hash_map::DefaultHasher::new();
opts.hash(&mut hasher);
hasher.finish()
};
let key = format!("{}:{:x}", tv, opts_hash);
let cache_file = format!("exec_env_{:x}.msgpack.z", opts_hash);
if !self.exec_env_cache.read().await.contains_key(&key) {
let mut caches = self.exec_env_cache.write().await;
caches.insert(
key.clone(),
CacheManagerBuilder::new(tv.cache_path().join("exec_env.msgpack.z"))
CacheManagerBuilder::new(tv.cache_path().join(&cache_file))
.with_fresh_file(dirs::DATA.to_path_buf())
.with_fresh_file(self.plugin.plugin_path.to_path_buf())
.with_fresh_file(self.ba().installs_path.to_path_buf())
Expand All @@ -271,7 +279,8 @@ impl VfoxBackend {
.await
.wrap_err("Backend exec env method failed")?
} else {
vfox.env_keys(&self.pathname, &tv.version).await?
vfox.env_keys(&self.pathname, &tv.version, &opts.opts)
.await?
Comment thread
cursor[bot] marked this conversation as resolved.
};

Ok(env_keys
Expand Down
Loading