feat(client): prompt for URL/CA when specifying a new profile name

This commit is contained in:
jasonwitty 2025-08-21 12:56:11 -07:00
parent 2af08c455a
commit b727e54589
2 changed files with 34 additions and 5 deletions

View File

@ -98,7 +98,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
// Determine final connection parameters (and maybe mutated profiles to persist) // Determine final connection parameters (and maybe mutated profiles to persist)
let mut profiles_mut = profiles_file.clone(); let mut profiles_mut = profiles_file.clone();
let (url, tls_ca): (String, Option<String>) = match resolved { let (url, tls_ca): (String, Option<String>) = match resolved {
ResolveProfile::Direct(u, t) => { ResolveProfile::Direct(u, t) => {
// Possibly save if profile specified and --save or new entry // Possibly save if profile specified and --save or new entry
if let Some(name) = parsed.profile.as_ref() { if let Some(name) = parsed.profile.as_ref() {
let existing = profiles_mut.profiles.get(name); let existing = profiles_mut.profiles.get(name);
@ -107,7 +107,10 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
// New profile: auto-save immediately // New profile: auto-save immediately
profiles_mut.profiles.insert( profiles_mut.profiles.insert(
name.clone(), name.clone(),
ProfileEntry { url: u.clone(), tls_ca: t.clone() }, ProfileEntry {
url: u.clone(),
tls_ca: t.clone(),
},
); );
let _ = save_profiles(&profiles_mut); let _ = save_profiles(&profiles_mut);
} }
@ -117,7 +120,10 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
if parsed.save { if parsed.save {
profiles_mut.profiles.insert( profiles_mut.profiles.insert(
name.clone(), name.clone(),
ProfileEntry { url: u.clone(), tls_ca: t.clone() }, ProfileEntry {
url: u.clone(),
tls_ca: t.clone(),
},
); );
let _ = save_profiles(&profiles_mut); let _ = save_profiles(&profiles_mut);
} else if prompt_yes_no(&format!( } else if prompt_yes_no(&format!(
@ -125,7 +131,10 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
)) { )) {
profiles_mut.profiles.insert( profiles_mut.profiles.insert(
name.clone(), name.clone(),
ProfileEntry { url: u.clone(), tls_ca: t.clone() }, ProfileEntry {
url: u.clone(),
tls_ca: t.clone(),
},
); );
let _ = save_profiles(&profiles_mut); let _ = save_profiles(&profiles_mut);
} // else: do not overwrite, just connect with provided details } // else: do not overwrite, just connect with provided details
@ -163,6 +172,16 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
return Ok(()); return Ok(());
} }
} }
ResolveProfile::PromptCreate(name) => {
eprintln!("Profile '{name}' does not exist yet.");
let url = prompt_string("Enter URL (ws://HOST:PORT/ws or wss://...): ")?;
if url.trim().is_empty() { return Ok(()); }
let ca = prompt_string("Enter TLS CA path (or leave blank): ")?;
let ca_opt = if ca.trim().is_empty() { None } else { Some(ca.trim().to_string()) };
profiles_mut.profiles.insert(name.clone(), ProfileEntry { url: url.trim().to_string(), tls_ca: ca_opt.clone() });
let _ = save_profiles(&profiles_mut);
(url.trim().to_string(), ca_opt)
}
ResolveProfile::None => { ResolveProfile::None => {
eprintln!("No URL provided and no profiles to select."); eprintln!("No URL provided and no profiles to select.");
return Ok(()); return Ok(());
@ -183,3 +202,11 @@ fn prompt_yes_no(prompt: &str) -> bool {
false false
} }
} }
fn prompt_string(prompt: &str) -> io::Result<String> {
eprint!("{prompt}");
let _ = io::stderr().flush();
let mut line = String::new();
io::stdin().read_line(&mut line)?;
Ok(line)
}

View File

@ -57,6 +57,8 @@ pub enum ResolveProfile {
Loaded(String, Option<String>), Loaded(String, Option<String>),
/// Should prompt user to select among profile names /// Should prompt user to select among profile names
PromptSelect(Vec<String>), PromptSelect(Vec<String>),
/// Should prompt user to create a new profile (name)
PromptCreate(String),
/// No profile could be resolved (e.g., missing arguments) /// No profile could be resolved (e.g., missing arguments)
None, None,
} }
@ -75,7 +77,7 @@ impl ProfileRequest {
if let Some(entry) = pf.profiles.get(&name) { if let Some(entry) = pf.profiles.get(&name) {
return ResolveProfile::Loaded(entry.url.clone(), entry.tls_ca.clone()); return ResolveProfile::Loaded(entry.url.clone(), entry.tls_ca.clone());
} else { } else {
return ResolveProfile::None; return ResolveProfile::PromptCreate(name);
} }
} }
// Both provided -> direct (maybe later saved by caller) // Both provided -> direct (maybe later saved by caller)