diff --git a/src/repo/ssh.rs b/src/repo/ssh.rs index dbebdf1..63265ce 100644 --- a/src/repo/ssh.rs +++ b/src/repo/ssh.rs @@ -1,9 +1,42 @@ -use std::path::Path; +use std::fs; +use std::path::{Path, PathBuf}; use git2::{Cred, Error}; +use crate::SheepError; /// Returns the standard path to the user's ssh key. -pub fn default_ssh_key_path() -> String { - shellexpand::tilde("~/.ssh/id_rsa").to_string() +fn ssh_key_path() -> String { + find_best_ssh_key().expect("failed to find ssh key") +} + +fn find_best_ssh_key() -> Result { + let ssh_dir = shellexpand::tilde("~/.ssh"); + let ssh_dir_path = PathBuf::from(ssh_dir.as_ref()); + let file_names = ssh_file_names(ssh_dir_path)?; + + let best_file_name = find_best_key_name(file_names)?; + let mut base_path = PathBuf::from(ssh_dir.as_ref()); + base_path.push(best_file_name); + Ok(base_path.to_string_lossy().to_string()) +} + +fn ssh_file_names(ssh_dir_path: PathBuf) -> Result, SheepError> { + let names = fs::read_dir(ssh_dir_path) + ?.map(|res| res.map(|e| e.file_name())) + .map({ + |res| res.map_or("".to_string(), + |name| name.to_string_lossy().to_string()) + }) + .collect::>(); + Ok(names) +} + +fn find_best_key_name(file_names: Vec) -> Result { + file_names.iter() + .find(|name| { + !name.ends_with(".pub") && name.starts_with("id_") + }) + .map(|name| name.to_string()) + .ok_or(SheepError::new("failed to find ssh key")) } pub fn add_credentials_to_callbacks(remote_callbacks: &mut git2::RemoteCallbacks) { @@ -16,20 +49,35 @@ pub fn create_ssh_key(username_from_url: &str) -> Result { Cred::ssh_key( username_from_url, None, - Path::new(&default_ssh_key_path()), + Path::new(&ssh_key_path()), None, ) } #[cfg(test)] mod test { - use std::env; - use crate::repo::ssh::default_ssh_key_path; + use crate::repo::ssh::find_best_key_name; + + #[test] + fn find_best_key_name_empty_names() { + let result = find_best_key_name(vec![]); + result.expect_err("should be an error"); + } #[test] - fn default_path_expands_tilde() { - let home = env::var("HOME").unwrap(); - let expected = format!("{home}/.ssh/id_rsa"); - assert_eq!(expected, default_ssh_key_path()) + fn find_best_key_name_typical_ssh() { + let names = vec![ + "config", + "id_rsa", + "id_rsa.pub", + "known_hosts", + ]; + let result = find_best_key_name( + names.iter() + .map(|n| n.to_string()) + .collect() + ); + let name = result.expect("failed to find file name"); + assert_eq!("id_rsa", name) } } \ No newline at end of file