use std::path::PathBuf;
use std::fs;
use std::env;
use crate::error::Result;
pub struct SshHelper;
impl SshHelper {
pub fn has_ssh_keys() -> bool {
let ssh_dir = Self::ssh_dir();
if !ssh_dir.exists() {
return false;
}
let key_files = vec![
"id_rsa",
"id_ed25519",
"id_ecdsa",
"id_dsa",
];
key_files.iter().any(|key| {
ssh_dir.join(key).exists()
})
}
pub fn ssh_dir() -> PathBuf {
let home = env::var("HOME")
.or_else(|_| env::var("USERPROFILE"))
.unwrap_or_else(|_| ".".to_string());
PathBuf::from(home).join(".ssh")
}
pub fn list_keys() -> Vec<String> {
let ssh_dir = Self::ssh_dir();
if !ssh_dir.exists() {
return vec![];
}
let key_files = vec![
"id_rsa",
"id_ed25519",
"id_ecdsa",
"id_dsa",
];
key_files
.iter()
.filter(|key| ssh_dir.join(key).exists())
.map(|s| s.to_string())
.collect()
}
#[allow(dead_code)]
pub fn get_public_key(key_name: &str) -> Result<String> {
let ssh_dir = Self::ssh_dir();
let pub_key_path = ssh_dir.join(format!("{}.pub", key_name));
if !pub_key_path.exists() {
return Ok(String::new());
}
Ok(fs::read_to_string(pub_key_path)?)
}
#[allow(dead_code)]
pub fn recommend_protocol() -> &'static str {
if Self::has_ssh_keys() {
"ssh"
} else {
"https"
}
}
#[allow(dead_code)]
pub fn get_setup_instructions() -> String {
let mut instructions = String::new();
instructions.push_str("📚 SSH Setup Instructions:\n\n");
instructions.push_str("1. Generate SSH key:\n");
instructions.push_str(" ssh-keygen -t ed25519 -C \"your_email@example.com\"\n\n");
instructions.push_str("2. Start SSH agent:\n");
instructions.push_str(" eval \"$(ssh-agent -s)\"\n");
instructions.push_str(" ssh-add ~/.ssh/id_ed25519\n\n");
instructions.push_str("3. Copy public key:\n");
instructions.push_str(" cat ~/.ssh/id_ed25519.pub\n\n");
instructions.push_str("4. Add to your platform:\n");
instructions.push_str(" • GitHub: https://github.com/settings/keys\n");
instructions.push_str(" • GitLab: https://gitlab.com/-/profile/keys\n");
instructions.push_str(" • Bitbucket: https://bitbucket.org/account/settings/ssh-keys/\n");
instructions
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_ssh_dir() {
let dir = SshHelper::ssh_dir();
assert!(dir.to_string_lossy().contains(".ssh"));
}
#[test]
fn test_recommend_protocol() {
let protocol = SshHelper::recommend_protocol();
assert!(protocol == "ssh" || protocol == "https");
}
}