use serde::Deserialize;
use std::collections::HashMap;
use std::path::PathBuf;
#[derive(Debug, Clone, Deserialize)]
pub struct ExtraKnownMarketplace {
pub source: String,
pub repo: String,
}
#[derive(Debug, Deserialize)]
struct ParsedSettings {
#[serde(default)]
enabled_plugins: Option<HashMap<String, serde_json::Value>>,
#[serde(default)]
extra_known_marketplaces: Option<HashMap<String, ExtraKnownMarketplace>>,
}
const SETTINGS_FILES: &[&str] = &["settings.json", "settings.local.json"];
pub fn get_add_dir_enabled_plugins() -> HashMap<String, serde_json::Value> {
let mut result: HashMap<String, serde_json::Value> = HashMap::new();
for dir in get_additional_directories_for_claude_md() {
for file in SETTINGS_FILES {
let path = dir.join(".ai").join(file);
if let Ok(content) = std::fs::read_to_string(&path) {
if let Ok(parsed) = serde_json::from_str::<ParsedSettings>(&content) {
if let Some(enabled_plugins) = parsed.enabled_plugins {
result.extend(enabled_plugins);
}
}
}
}
}
result
}
pub fn get_add_dir_extra_marketplaces() -> HashMap<String, ExtraKnownMarketplace> {
let mut result: HashMap<String, ExtraKnownMarketplace> = HashMap::new();
for dir in get_additional_directories_for_claude_md() {
for file in SETTINGS_FILES {
let path = dir.join(".ai").join(file);
if let Ok(content) = std::fs::read_to_string(&path) {
if let Ok(parsed) = serde_json::from_str::<ParsedSettings>(&content) {
if let Some(extra_marketplaces) = parsed.extra_known_marketplaces {
result.extend(extra_marketplaces);
}
}
}
}
}
result
}
fn get_additional_directories_for_claude_md() -> Vec<PathBuf> {
if let Ok(dirs_str) = std::env::var("AI_CODE_ADDITIONAL_DIRS") {
dirs_str
.split('|')
.filter(|s| !s.is_empty())
.map(PathBuf::from)
.collect()
} else {
Vec::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_empty_when_no_dirs() {
#[allow(unused_unsafe)]
unsafe {
std::env::remove_var("AI_CODE_ADDITIONAL_DIRS");
}
assert!(get_add_dir_enabled_plugins().is_empty());
assert!(get_add_dir_extra_marketplaces().is_empty());
}
}