claude_code_switcher/
simple_selector.rs1use crate::{
2 credentials::{CredentialStore, SavedCredential},
3 templates::{TemplateType, get_template_instance},
4};
5use anyhow::{Result, anyhow};
6use inquire::{Select, Text};
7
8pub struct SimpleCredentialSelector {
10 credentials: Vec<SavedCredential>,
11 template_type: TemplateType,
12}
13
14impl SimpleCredentialSelector {
15 pub fn new(credentials: Vec<SavedCredential>, template_type: TemplateType) -> Self {
16 Self {
17 credentials,
18 template_type,
19 }
20 }
21
22 pub fn run(&mut self) -> Result<Option<String>> {
23 if self.credentials.is_empty() {
24 let template_instance = get_template_instance(&self.template_type);
26 if let Some(url) = template_instance.api_key_url() {
27 println!(" 💡 Get your API key from: {}", url);
28 }
29
30 let prompt_text = format!("Enter your {} API key:", self.template_type);
32 let api_key = Text::new(&prompt_text)
33 .with_placeholder("sk-...")
34 .prompt()
35 .map_err(|e| anyhow!("Failed to get API key: {}", e))?;
36
37 if !api_key.trim().is_empty() {
38 return Ok(Some(api_key));
39 }
40 return Ok(None);
41 }
42
43 let mut options = Vec::new();
45
46 for credential in &self.credentials {
48 let masked_key = mask_api_key(credential.api_key());
49 options.push(format!("{} ({})", credential.name(), masked_key));
50 }
51
52 options.push("Enter new API key...".to_string());
54
55 let prompt_text = format!("Select {} API key:", self.template_type);
57 match Select::new(&prompt_text, options).prompt() {
58 Ok(choice) => {
59 if choice == "Enter new API key..." {
60 let template_instance = get_template_instance(&self.template_type);
62 if let Some(url) = template_instance.api_key_url() {
63 println!(" 💡 Get your API key from: {}", url);
64 }
65
66 let prompt_text = format!("Enter your {} API key:", self.template_type);
68 let api_key = Text::new(&prompt_text)
69 .with_placeholder("sk-...")
70 .prompt()
71 .map_err(|e| anyhow!("Failed to get API key: {}", e))?;
72
73 if !api_key.trim().is_empty() {
74 return Ok(Some(api_key));
75 }
76 Err(anyhow!("API key cannot be empty"))
77 } else {
78 let index = self.find_credential_index(&choice)?;
80 Ok(Some(self.credentials[index].api_key().to_string()))
81 }
82 }
83 Err(_) => Ok(None), }
85 }
86
87 fn find_credential_index(&self, choice: &str) -> Result<usize> {
88 for (i, credential) in self.credentials.iter().enumerate() {
89 let masked_key = mask_api_key(credential.api_key());
90 let option_text = format!("{} ({})", credential.name(), masked_key);
91 if option_text == choice {
92 return Ok(i);
93 }
94 }
95 Err(anyhow!("Credential not found"))
96 }
97}
98
99pub fn get_endpoint_id_interactively(template_type: &TemplateType) -> Result<String> {
101 let endpoint_ids = if let Ok(credential_store) = CredentialStore::new() {
103 credential_store.get_endpoint_ids(template_type)
104 } else {
105 Vec::new()
106 };
107
108 if endpoint_ids.is_empty() {
109 let template_instance = get_template_instance(template_type);
111 if let Some(url) = template_instance.api_key_url() {
112 println!(" 💡 Get your API key from: {}", url);
113 }
114
115 let prompt_text = format!("Enter {} endpoint ID:", template_type);
117 let endpoint_id = Text::new(&prompt_text)
118 .with_placeholder("ep-xxx-xxx")
119 .prompt()
120 .map_err(|e| anyhow!("Failed to get endpoint ID: {}", e))?;
121
122 if !endpoint_id.trim().is_empty() {
123 return Ok(endpoint_id);
124 }
125 return Err(anyhow!("Endpoint ID cannot be empty"));
126 }
127
128 let mut options = Vec::new();
130
131 for (display_name, _endpoint_id) in &endpoint_ids {
133 options.push(display_name.clone());
134 }
135
136 options.push("Enter new endpoint ID...".to_string());
138
139 let prompt_text = format!("Select {} endpoint ID:", template_type);
141 match Select::new(&prompt_text, options).prompt() {
142 Ok(choice) => {
143 if choice == "Enter new endpoint ID..." {
144 let template_instance = get_template_instance(template_type);
146 if let Some(url) = template_instance.api_key_url() {
147 println!(" 💡 Get your API key from: {}", url);
148 }
149
150 let prompt_text = format!("Enter {} endpoint ID:", template_type);
152 let endpoint_id = Text::new(&prompt_text)
153 .with_placeholder("ep-xxx-xxx")
154 .prompt()
155 .map_err(|e| anyhow!("Failed to get endpoint ID: {}", e))?;
156
157 if !endpoint_id.trim().is_empty() {
158 return Ok(endpoint_id);
159 }
160 Err(anyhow!("Endpoint ID cannot be empty"))
161 } else {
162 for (display_name, endpoint_id) in &endpoint_ids {
164 if display_name == &choice {
165 return Ok(endpoint_id.clone());
166 }
167 }
168 Err(anyhow!("Endpoint ID not found"))
169 }
170 }
171 Err(_) => Err(anyhow!("No endpoint ID selected")), }
173}
174
175fn mask_api_key(api_key: &str) -> String {
176 if api_key.len() <= 8 {
177 "••••••••".to_string()
178 } else {
179 format!(
180 "{}{}{}",
181 &api_key[..4],
182 "•".repeat(api_key.len() - 8),
183 &api_key[api_key.len() - 4..]
184 )
185 }
186}