use embacle::config::CliRunnerType;
use crate::runner::parse_runner_type;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ResolvedProvider {
pub runner_type: CliRunnerType,
pub model: Option<String>,
}
pub fn resolve_model(model_str: &str, default_provider: CliRunnerType) -> ResolvedProvider {
if let Some((prefix, model)) = model_str.split_once(':') {
if let Some(runner_type) = parse_runner_type(prefix) {
return ResolvedProvider {
runner_type,
model: if model.is_empty() {
None
} else {
Some(model.to_owned())
},
};
}
ResolvedProvider {
runner_type: default_provider,
model: Some(model_str.to_owned()),
}
} else if let Some(runner_type) = parse_runner_type(model_str) {
ResolvedProvider {
runner_type,
model: None,
}
} else {
ResolvedProvider {
runner_type: default_provider,
model: Some(model_str.to_owned()),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn resolve_provider_with_model() {
let result = resolve_model("copilot:gpt-4o", CliRunnerType::ClaudeCode);
assert_eq!(result.runner_type, CliRunnerType::Copilot);
assert_eq!(result.model.as_deref(), Some("gpt-4o"));
}
#[test]
fn resolve_claude_with_model() {
let result = resolve_model("claude:opus", CliRunnerType::Copilot);
assert_eq!(result.runner_type, CliRunnerType::ClaudeCode);
assert_eq!(result.model.as_deref(), Some("opus"));
}
#[test]
fn resolve_provider_only() {
let result = resolve_model("copilot", CliRunnerType::ClaudeCode);
assert_eq!(result.runner_type, CliRunnerType::Copilot);
assert!(result.model.is_none());
}
#[test]
fn resolve_bare_model_uses_default() {
let result = resolve_model("gpt-4o", CliRunnerType::Copilot);
assert_eq!(result.runner_type, CliRunnerType::Copilot);
assert_eq!(result.model.as_deref(), Some("gpt-4o"));
}
#[test]
fn resolve_provider_with_empty_model() {
let result = resolve_model("copilot:", CliRunnerType::ClaudeCode);
assert_eq!(result.runner_type, CliRunnerType::Copilot);
assert!(result.model.is_none());
}
#[test]
fn resolve_unknown_prefix_as_bare_model() {
let result = resolve_model("unknown:something", CliRunnerType::Copilot);
assert_eq!(result.runner_type, CliRunnerType::Copilot);
assert_eq!(result.model.as_deref(), Some("unknown:something"));
}
#[test]
fn resolve_case_insensitive_provider() {
let result = resolve_model("CLAUDE:opus", CliRunnerType::Copilot);
assert_eq!(result.runner_type, CliRunnerType::ClaudeCode);
assert_eq!(result.model.as_deref(), Some("opus"));
}
#[test]
fn resolve_cursor_agent_variants() {
for prefix in &["cursor_agent", "cursor-agent", "cursoragent"] {
let model_str = format!("{prefix}:model");
let result = resolve_model(&model_str, CliRunnerType::Copilot);
assert_eq!(result.runner_type, CliRunnerType::CursorAgent);
assert_eq!(result.model.as_deref(), Some("model"));
}
}
#[test]
fn resolve_opencode_variants() {
let result = resolve_model("opencode:latest", CliRunnerType::Copilot);
assert_eq!(result.runner_type, CliRunnerType::OpenCode);
assert_eq!(result.model.as_deref(), Some("latest"));
let result = resolve_model("open_code:latest", CliRunnerType::Copilot);
assert_eq!(result.runner_type, CliRunnerType::OpenCode);
assert_eq!(result.model.as_deref(), Some("latest"));
}
}