use std::path::Path;
use crate::error::{AugentError, Result};
use super::{Platform, loader::PlatformLoader};
pub fn detect_platforms(workspace_root: &Path) -> Result<Vec<Platform>> {
if !workspace_root.exists() {
return Err(AugentError::WorkspaceNotFound {
path: workspace_root.display().to_string(),
});
}
let loader = PlatformLoader::new(workspace_root);
let platforms = loader.load()?;
let detected: Vec<Platform> = platforms
.into_iter()
.filter(|p| workspace_root.join(&p.directory).exists())
.collect();
Ok(detected)
}
#[allow(dead_code)] pub fn detect_platforms_or_error(workspace_root: &Path) -> Result<Vec<Platform>> {
let platforms = detect_platforms(workspace_root)?;
if platforms.is_empty() {
return Err(AugentError::NoPlatformsDetected);
}
Ok(platforms)
}
pub fn get_platform(id: &str, workspace_root: Option<&Path>) -> Option<Platform> {
let platforms = if let Some(root) = workspace_root {
PlatformLoader::new(root).load().ok().unwrap_or_default()
} else {
if let Ok(current_dir) = std::env::current_dir() {
PlatformLoader::new(¤t_dir)
.load()
.ok()
.unwrap_or_default()
} else {
super::default_platforms()
}
};
if let Some(platform) = platforms.iter().find(|p| p.id == id) {
return Some(platform.clone());
}
let alias_id = match id {
"cursor-ai" => "cursor",
_ => return None,
};
platforms.iter().find(|p| p.id == alias_id).cloned()
}
pub fn get_platforms(ids: &[String], workspace_root: Option<&Path>) -> Result<Vec<Platform>> {
let mut platforms = Vec::new();
for id in ids {
match get_platform(id, workspace_root) {
Some(p) => platforms.push(p),
None => {
return Err(AugentError::PlatformNotSupported {
platform: id.clone(),
});
}
}
}
Ok(platforms)
}
#[allow(dead_code)] pub fn resolve_platforms(workspace_root: &Path, specified: &[String]) -> Result<Vec<Platform>> {
if specified.is_empty() {
detect_platforms_or_error(workspace_root)
} else {
get_platforms(specified, Some(workspace_root))
}
}
#[cfg(test)]
mod tests {
use super::*;
use tempfile::TempDir;
#[test]
fn test_detect_platforms_empty() {
let temp = TempDir::new_in(crate::temp::temp_dir_base()).unwrap();
let platforms = detect_platforms(temp.path()).unwrap();
assert!(platforms.is_empty());
}
#[test]
fn test_detect_platforms_claude() {
let temp = TempDir::new_in(crate::temp::temp_dir_base()).unwrap();
std::fs::create_dir(temp.path().join(".claude")).unwrap();
let platforms = detect_platforms(temp.path()).unwrap();
assert_eq!(platforms.len(), 1);
assert_eq!(platforms[0].id, "claude");
}
#[test]
fn test_detect_platforms_multiple() {
let temp = TempDir::new_in(crate::temp::temp_dir_base()).unwrap();
std::fs::create_dir(temp.path().join(".claude")).unwrap();
std::fs::create_dir(temp.path().join(".cursor")).unwrap();
let platforms = detect_platforms(temp.path()).unwrap();
assert_eq!(platforms.len(), 2);
}
#[test]
fn test_root_agent_file_adds_no_platform() {
let temp = TempDir::new_in(crate::temp::temp_dir_base()).unwrap();
std::fs::write(temp.path().join("CLAUDE.md"), "# Claude").unwrap();
let platforms = detect_platforms(temp.path()).unwrap();
assert!(
platforms.is_empty(),
"root agent files (CLAUDE.md, AGENTS.md, etc.) must not add any platform"
);
}
#[test]
fn test_detect_platforms_or_error_empty() {
let temp = TempDir::new_in(crate::temp::temp_dir_base()).unwrap();
let result = detect_platforms_or_error(temp.path());
assert!(result.is_err());
}
#[test]
fn test_get_platform() {
let claude = get_platform("claude", None);
assert!(claude.is_some());
assert_eq!(claude.unwrap().id, "claude");
let unknown = get_platform("unknown", None);
assert!(unknown.is_none());
}
#[test]
fn test_get_platforms() {
let platforms = get_platforms(&["claude".to_string(), "cursor".to_string()], None).unwrap();
assert_eq!(platforms.len(), 2);
}
#[test]
fn test_get_platforms_unknown() {
let result = get_platforms(&["unknown".to_string()], None);
assert!(result.is_err());
}
#[test]
fn test_resolve_platforms_specified() {
let temp = TempDir::new_in(crate::temp::temp_dir_base()).unwrap();
let platforms = resolve_platforms(temp.path(), &["claude".to_string()]).unwrap();
assert_eq!(platforms.len(), 1);
assert_eq!(platforms[0].id, "claude");
}
#[test]
fn test_resolve_platforms_auto_detect() {
let temp = TempDir::new_in(crate::temp::temp_dir_base()).unwrap();
std::fs::create_dir(temp.path().join(".opencode")).unwrap();
let platforms = resolve_platforms(temp.path(), &[]).unwrap();
assert_eq!(platforms.len(), 1);
assert_eq!(platforms[0].id, "opencode");
}
#[test]
fn test_get_platform_resolves_alias() {
let cursor = get_platform("cursor", None);
assert!(cursor.is_some());
assert_eq!(cursor.clone().unwrap().id, "cursor");
let cursor_ai = get_platform("cursor-ai", None);
assert!(cursor_ai.is_some());
assert_eq!(cursor_ai.clone().unwrap().id, "cursor");
assert_eq!(cursor_ai.clone().unwrap().name, "Cursor");
}
#[test]
fn test_get_platforms_resolves_aliases() {
let platforms =
get_platforms(&["cursor-ai".to_string(), "claude".to_string()], None).unwrap();
assert_eq!(platforms.len(), 2);
assert_eq!(platforms[0].id, "cursor");
assert_eq!(platforms[0].name, "Cursor");
assert_eq!(platforms[1].id, "claude");
assert_eq!(platforms[1].name, "Claude Code");
}
#[test]
fn test_get_platform_unknown_alias() {
let unknown = get_platform("unknown-alias", None);
assert!(unknown.is_none());
}
}