1use crate::{Fingerprint, PrivateKey, PublicKey, Result};
4use std::{
5 fs::{self, ReadDir},
6 path::{Path, PathBuf},
7};
8
9#[derive(Clone, Eq, PartialEq)]
11pub struct DotSsh {
12 path: PathBuf,
13}
14
15impl DotSsh {
16 pub fn new() -> Option<Self> {
20 home::home_dir().map(|path| Self::open(path.join(".ssh")))
21 }
22
23 pub fn open(path: impl Into<PathBuf>) -> Self {
29 let path = path.into();
30 Self {
31 path: path.canonicalize().unwrap_or(path),
32 }
33 }
34
35 pub fn path(&self) -> &Path {
37 &self.path
38 }
39
40 pub fn config_path(&self) -> PathBuf {
42 self.path.join("config")
43 }
44
45 pub fn private_keys(&self) -> Result<impl Iterator<Item = PrivateKey>> {
47 Ok(PrivateKeysIter {
48 read_dir: fs::read_dir(&self.path)?,
49 })
50 }
51
52 pub fn private_key_with_fingerprint(&self, fingerprint: Fingerprint) -> Option<PrivateKey> {
54 self.private_keys()
55 .ok()?
56 .find(|key| key.public_key().fingerprint(fingerprint.algorithm()) == fingerprint)
57 }
58
59 pub fn public_keys(&self) -> Result<impl Iterator<Item = PublicKey>> {
61 Ok(PublicKeysIter {
62 read_dir: fs::read_dir(&self.path)?,
63 })
64 }
65
66 pub fn public_key_with_fingerprint(&self, fingerprint: Fingerprint) -> Option<PublicKey> {
68 self.public_keys()
69 .ok()?
70 .find(|key| key.fingerprint(fingerprint.algorithm()) == fingerprint)
71 }
72
73 pub fn write_private_key(&self, filename: impl AsRef<Path>, key: &PrivateKey) -> Result<()> {
75 key.write_openssh_file(self.path.join(filename), Default::default())
76 }
77
78 pub fn write_public_key(&self, filename: impl AsRef<Path>, key: &PublicKey) -> Result<()> {
80 key.write_openssh_file(self.path.join(filename))
81 }
82}
83
84impl Default for DotSsh {
85 fn default() -> Self {
87 Self::new().expect("home directory could not be located")
88 }
89}
90
91pub struct PrivateKeysIter {
93 read_dir: ReadDir,
94}
95
96impl Iterator for PrivateKeysIter {
97 type Item = PrivateKey;
98
99 fn next(&mut self) -> Option<Self::Item> {
100 loop {
101 let entry = self.read_dir.next()?.ok()?;
102
103 if let Ok(key) = PrivateKey::read_openssh_file(entry.path()) {
104 return Some(key);
105 }
106 }
107 }
108}
109
110pub struct PublicKeysIter {
112 read_dir: ReadDir,
113}
114
115impl Iterator for PublicKeysIter {
116 type Item = PublicKey;
117
118 fn next(&mut self) -> Option<Self::Item> {
119 loop {
120 let entry = self.read_dir.next()?.ok()?;
121
122 if let Ok(key) = PublicKey::read_openssh_file(entry.path()) {
123 return Some(key);
124 }
125 }
126 }
127}