1use std::path::PathBuf;
2
3#[derive(Debug, Clone)]
5pub enum RecipientKey {
6 Ssh(String),
8 Age(String),
10}
11
12impl RecipientKey {
13 pub fn from_ssh_file(path: &PathBuf) -> crate::error::Result<Self> {
15 let content = std::fs::read_to_string(path).map_err(crate::error::MnemeError::Io)?;
16 Ok(RecipientKey::Ssh(content.trim().to_string()))
17 }
18
19 pub fn from_string(s: &str) -> crate::error::Result<Self> {
21 let s = s.trim();
22 if s.starts_with("age1") {
23 Ok(RecipientKey::Age(s.to_string()))
24 } else {
25 Ok(RecipientKey::Ssh(s.to_string()))
26 }
27 }
28
29 pub fn key_type(&self) -> &str {
31 match self {
32 RecipientKey::Ssh(s) => {
33 if s.contains("ssh-ed25519") {
34 "ssh-ed25519"
35 } else if s.contains("ssh-rsa") {
36 "ssh-rsa"
37 } else {
38 "ssh"
39 }
40 }
41 RecipientKey::Age(_) => "age",
42 }
43 }
44
45 pub fn public_key_string(&self) -> String {
47 match self {
48 RecipientKey::Ssh(s) | RecipientKey::Age(s) => s.clone(),
49 }
50 }
51}
52
53#[derive(Debug)]
55pub enum IdentityKey {
56 Ssh(PathBuf),
58 Age(PathBuf),
60}
61
62impl IdentityKey {
63 pub fn detect() -> crate::error::Result<Self> {
65 if let Ok(val) = std::env::var("MNEME_IDENTITY") {
67 return Self::from_path(&PathBuf::from(val));
68 }
69 if let Some(mut home) = dirs::home_dir() {
71 home.push(".ssh");
72 let ed25519 = home.join("id_ed25519");
73 if ed25519.exists() {
74 return Ok(IdentityKey::Ssh(ed25519));
75 }
76 let rsa = home.join("id_rsa");
78 if rsa.exists() {
79 return Ok(IdentityKey::Ssh(rsa));
80 }
81 }
82 if let Some(mut home) = dirs::home_dir() {
84 home.push(".age");
85 let key = home.join("key.txt");
86 if key.exists() {
87 return Ok(IdentityKey::Age(key));
88 }
89 }
90 Err(crate::error::MnemeError::IdentityNotLoaded)
91 }
92
93 pub fn from_path(path: &PathBuf) -> crate::error::Result<Self> {
95 if !path.exists() {
96 return Err(crate::error::MnemeError::Io(std::io::Error::new(
97 std::io::ErrorKind::NotFound,
98 format!("identity file not found: {}", path.display()),
99 )));
100 }
101 if path.extension().and_then(|e| e.to_str()) == Some("txt") {
103 return Ok(IdentityKey::Age(path.clone()));
104 }
105 let filename = path.file_name().and_then(|n| n.to_str()).unwrap_or("");
106 if filename.starts_with("id_") {
107 Ok(IdentityKey::Ssh(path.clone()))
108 } else {
109 let content = std::fs::read_to_string(path)?;
111 if content.contains("AGE-SECRET-KEY") {
112 Ok(IdentityKey::Age(path.clone()))
113 } else {
114 Ok(IdentityKey::Ssh(path.clone()))
115 }
116 }
117 }
118
119 pub fn path(&self) -> &PathBuf {
121 match self {
122 IdentityKey::Ssh(p) | IdentityKey::Age(p) => p,
123 }
124 }
125}