use crate::selectors::{
base::SelectableItem,
error::{SelectorError, SelectorResult},
navigation::NavigationManager,
};
use crate::{
credentials::CredentialStore,
templates::{TemplateType, get_template_instance},
};
pub struct TemplateSelector;
impl TemplateSelector {
pub fn select_template() -> SelectorResult<TemplateType> {
let template_types = vec![
TemplateType::DeepSeek,
TemplateType::Zai,
TemplateType::KatCoder,
TemplateType::Kimi,
TemplateType::Longcat,
TemplateType::Fishtrip,
TemplateType::MiniMax,
TemplateType::SeedCode,
TemplateType::Zenmux,
TemplateType::Duojie,
TemplateType::AnyRouter,
];
let items: Vec<TemplateItem> = template_types.into_iter().map(TemplateItem::new).collect();
match NavigationManager::select_from_list(
&items,
"Select AI provider:",
false,
Some("↑/↓: Navigate, →: Select, ←/Esc: Back"),
)? {
crate::selectors::navigation::NavigationResult::Selected(item) => {
Ok(item.template_type)
}
crate::selectors::navigation::NavigationResult::Back
| crate::selectors::navigation::NavigationResult::Exit
| crate::selectors::navigation::NavigationResult::CreateNew => {
Err(SelectorError::Cancelled)
}
}
}
pub fn get_api_key_for_template(template_type: TemplateType) -> SelectorResult<Option<String>> {
crate::selectors::credential::CredentialSelector::select_api_key(template_type)
}
pub fn get_endpoint_id_for_template(
template_type: &TemplateType,
) -> SelectorResult<Option<String>> {
let template_instance = get_template_instance(template_type);
if !template_instance.requires_additional_config() {
return Ok(None);
}
let endpoint_ids = if let Ok(credential_store) = CredentialStore::new() {
credential_store.get_endpoint_ids(template_type)
} else {
Vec::new()
};
if endpoint_ids.is_empty() {
if let Some(url) = template_instance.api_key_url() {
println!(" 💡 Get your endpoint ID from: {}", url);
}
let prompt_text = format!("Enter {} endpoint ID:", template_type);
let endpoint_id =
NavigationManager::get_text_input(&prompt_text, Some("ep-xxx-xxx"), None)?;
if !endpoint_id.trim().is_empty() {
return Ok(Some(endpoint_id));
}
return Err(SelectorError::InvalidInput(
"Endpoint ID cannot be empty".to_string(),
));
}
let items: Vec<EndpointItem> = endpoint_ids
.into_iter()
.map(|(display_name, endpoint_id)| EndpointItem {
display_name,
endpoint_id,
})
.collect();
let mut options = vec!["Enter new endpoint ID...".to_string()];
for item in &items {
options.push(item.display_name.clone());
}
let selection = NavigationManager::select_option(
&format!("Select {} endpoint ID:", template_type),
&options.iter().map(|s| s.as_str()).collect::<Vec<_>>(),
Some("↑/↓: Navigate, →: Select, ←/Esc: Back"),
)?;
if selection == "Enter new endpoint ID..." {
if let Some(url) = template_instance.api_key_url() {
println!(" 💡 Get your endpoint ID from: {}", url);
}
let prompt_text = format!("Enter {} endpoint ID:", template_type);
let endpoint_id =
NavigationManager::get_text_input(&prompt_text, Some("ep-xxx-xxx"), None)?;
if !endpoint_id.trim().is_empty() {
Ok(Some(endpoint_id))
} else {
Err(SelectorError::InvalidInput(
"Endpoint ID cannot be empty".to_string(),
))
}
} else {
for item in items {
if item.display_name == selection {
return Ok(Some(item.endpoint_id));
}
}
Err(SelectorError::NotFound)
}
}
}
#[derive(Debug, Clone)]
struct TemplateItem {
template_type: TemplateType,
}
impl TemplateItem {
fn new(template_type: TemplateType) -> Self {
Self { template_type }
}
}
impl SelectableItem for TemplateItem {
fn display_name(&self) -> String {
format!("{}", self.template_type)
}
fn format_for_list(&self) -> String {
let template_instance = get_template_instance(&self.template_type);
let env_vars = template_instance.env_var_names();
let env_indicator = if env_vars.len() > 1 {
format!(" (+{})", env_vars.len())
} else {
String::new()
};
format!("{} ({:?}){}", self.template_type, env_vars, env_indicator)
}
fn id(&self) -> Option<String> {
Some(format!("{:?}", self.template_type))
}
}
#[derive(Debug, Clone)]
struct EndpointItem {
display_name: String,
endpoint_id: String,
}
impl SelectableItem for EndpointItem {
fn display_name(&self) -> String {
self.display_name.clone()
}
fn format_for_list(&self) -> String {
self.display_name.clone()
}
fn id(&self) -> Option<String> {
Some(self.endpoint_id.clone())
}
}
pub fn get_endpoint_id_interactively(template_type: &TemplateType) -> SelectorResult<String> {
match TemplateSelector::get_endpoint_id_for_template(template_type)? {
Some(endpoint_id) => Ok(endpoint_id),
None => Err(SelectorError::NotFound),
}
}