use crate::{Fingerprint, PrivateKey, PublicKey, Result};
use std::{
fs::{self, ReadDir},
path::{Path, PathBuf},
};
#[derive(Clone, Eq, PartialEq)]
pub struct DotSsh {
path: PathBuf,
}
impl DotSsh {
pub fn new() -> Option<Self> {
home::home_dir().map(|path| Self::open(path.join(".ssh")))
}
pub fn open(path: impl Into<PathBuf>) -> Self {
let path = path.into();
Self {
path: path.canonicalize().unwrap_or(path),
}
}
pub fn path(&self) -> &Path {
&self.path
}
pub fn config_path(&self) -> PathBuf {
self.path.join("config")
}
pub fn private_keys(&self) -> Result<impl Iterator<Item = PrivateKey>> {
Ok(PrivateKeysIter {
read_dir: fs::read_dir(&self.path)?,
})
}
pub fn private_key_with_fingerprint(&self, fingerprint: Fingerprint) -> Option<PrivateKey> {
self.private_keys()
.ok()?
.find(|key| key.public_key().fingerprint(fingerprint.algorithm()) == fingerprint)
}
pub fn public_keys(&self) -> Result<impl Iterator<Item = PublicKey>> {
Ok(PublicKeysIter {
read_dir: fs::read_dir(&self.path)?,
})
}
pub fn public_key_with_fingerprint(&self, fingerprint: Fingerprint) -> Option<PublicKey> {
self.public_keys()
.ok()?
.find(|key| key.fingerprint(fingerprint.algorithm()) == fingerprint)
}
pub fn write_private_key(&self, filename: impl AsRef<Path>, key: &PrivateKey) -> Result<()> {
key.write_openssh_file(&self.path.join(filename), Default::default())
}
pub fn write_public_key(&self, filename: impl AsRef<Path>, key: &PublicKey) -> Result<()> {
key.write_openssh_file(&self.path.join(filename))
}
}
impl Default for DotSsh {
fn default() -> Self {
Self::new().expect("home directory could not be located")
}
}
pub struct PrivateKeysIter {
read_dir: ReadDir,
}
impl Iterator for PrivateKeysIter {
type Item = PrivateKey;
fn next(&mut self) -> Option<Self::Item> {
loop {
let entry = self.read_dir.next()?.ok()?;
if let Ok(key) = PrivateKey::read_openssh_file(&entry.path()) {
return Some(key);
}
}
}
}
pub struct PublicKeysIter {
read_dir: ReadDir,
}
impl Iterator for PublicKeysIter {
type Item = PublicKey;
fn next(&mut self) -> Option<Self::Item> {
loop {
let entry = self.read_dir.next()?.ok()?;
if let Ok(key) = PublicKey::read_openssh_file(&entry.path()) {
return Some(key);
}
}
}
}