use std::fs;
use std::path::PathBuf;
use super::types::{KnownMarketplacesFile, PluginMarketplace, PluginMarketplaceEntry};
use crate::utils::config::get_global_config_path;
fn get_known_marketplaces_file() -> PathBuf {
get_global_config_path().join("known_marketplaces.json")
}
async fn read_cached_marketplace(install_location: &str) -> Option<PluginMarketplace> {
let marketplace_path = PathBuf::from(install_location)
.join(".ai-plugin")
.join("marketplace.json");
if !marketplace_path.exists() {
return None;
}
match fs::read_to_string(&marketplace_path) {
Ok(content) => match serde_json::from_str::<PluginMarketplace>(&content) {
Ok(marketplace) => Some(marketplace),
Err(e) => {
eprintln!(
"Failed to parse marketplace at {}: {}",
marketplace_path.display(),
e
);
None
}
},
Err(e) => {
eprintln!(
"Failed to read marketplace at {}: {}",
marketplace_path.display(),
e
);
None
}
}
}
async fn load_known_marketplaces_config() -> Option<KnownMarketplacesFile> {
let config_file = get_known_marketplaces_file();
if !config_file.exists() {
return None;
}
match fs::read_to_string(&config_file) {
Ok(content) => match serde_json::from_str::<KnownMarketplacesFile>(&content) {
Ok(config) => Some(config),
Err(e) => {
eprintln!("Failed to parse known marketplaces: {}", e);
None
}
},
Err(e) => {
eprintln!("Failed to read known marketplaces file: {}", e);
None
}
}
}
pub fn parse_plugin_identifier(plugin_id: &str) -> (Option<String>, Option<String>) {
if let Some(at_pos) = plugin_id.rfind('@') {
let name = plugin_id[..at_pos].to_string();
let marketplace = plugin_id[at_pos + 1..].to_string();
if !name.is_empty() && !marketplace.is_empty() {
return (Some(name), Some(marketplace));
}
}
(None, None)
}
pub async fn get_marketplace_cache_only(name: &str) -> Option<PluginMarketplace> {
let config_file = get_known_marketplaces_file();
if !config_file.exists() {
return None;
}
match fs::read_to_string(&config_file) {
Ok(content) => {
match serde_json::from_str::<KnownMarketplacesFile>(&content) {
Ok(config) => {
if let Some(entry) = config.get(name) {
if let Some(marketplace) =
read_cached_marketplace(&entry.install_location).await
{
return Some(marketplace);
}
}
None
}
Err(e) => {
eprintln!("Failed to parse known marketplaces config: {}", e);
None
}
}
}
Err(_) => None,
}
}
pub async fn get_plugin_by_id_cache_only(
plugin_id: &str,
) -> Option<(PluginMarketplaceEntry, String)> {
let (plugin_name, marketplace_name) = parse_plugin_identifier(plugin_id);
let plugin_name = plugin_name?;
let marketplace_name = marketplace_name?;
let config_file = get_known_marketplaces_file();
if !config_file.exists() {
return None;
}
match fs::read_to_string(&config_file) {
Ok(content) => {
match serde_json::from_str::<KnownMarketplacesFile>(&content) {
Ok(config) => {
let marketplace_config = config.get(&marketplace_name)?;
let marketplace = get_marketplace_cache_only(&marketplace_name).await?;
marketplace
.plugins
.into_iter()
.find(|p| p.name == plugin_name)
.map(|entry| (entry, marketplace_config.install_location.clone()))
}
Err(_) => None,
}
}
Err(_) => None,
}
}
pub async fn get_known_marketplace_names() -> Vec<String> {
match load_known_marketplaces_config().await {
Some(config) => config.keys().cloned().collect(),
None => vec![],
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_parse_plugin_identifier_basic() {
let (name, marketplace) = parse_plugin_identifier("my-plugin@my-marketplace");
assert_eq!(name, Some("my-plugin".to_string()));
assert_eq!(marketplace, Some("my-marketplace".to_string()));
}
#[test]
fn test_parse_plugin_identifier_invalid() {
let (name, marketplace) = parse_plugin_identifier("invalid");
assert_eq!(name, None);
assert_eq!(marketplace, None);
}
#[test]
fn test_parse_plugin_identifier_empty_marketplace() {
let (name, marketplace) = parse_plugin_identifier("my-plugin@");
assert_eq!(name, None);
assert_eq!(marketplace, None);
}
}