1use std::path::PathBuf;
5use thiserror::Error;
6
7#[derive(Error, Debug)]
9pub enum SshError {
10 #[error("failed to read SSH key from {path}: {source}")]
12 ReadKey {
13 path: PathBuf,
14 #[source]
15 source: std::io::Error,
16 },
17
18 #[error("failed to parse SSH key from {path}: {source}")]
20 ParseKey {
21 path: PathBuf,
22 #[source]
23 source: ssh_key::Error,
24 },
25
26 #[error("failed to generate SSH key: {0}")]
28 GenerateKey(#[source] ssh_key::Error),
29
30 #[error("failed to serialize key: {0}")]
32 SerializeKey(#[source] ssh_key::Error),
33
34 #[error("failed to write key to {path}: {source}")]
36 WriteKey {
37 path: PathBuf,
38 #[source]
39 source: std::io::Error,
40 },
41
42 #[error("failed to create directory {path}: {source}")]
44 CreateDirectory {
45 path: PathBuf,
46 #[source]
47 source: std::io::Error,
48 },
49
50 #[error("failed to set permissions on {path}: {source}")]
52 SetPermissions {
53 path: PathBuf,
54 #[source]
55 source: std::io::Error,
56 },
57
58 #[error("unsupported key type: {0} (only ed25519 is supported)")]
60 UnsupportedKeyType(String),
61
62 #[error("invalid metadata value for {field}: {message}")]
64 InvalidMetadata { field: String, message: String },
65}
66
67pub type Result<T> = std::result::Result<T, SshError>;
69
70#[cfg(test)]
71mod tests {
72 use super::*;
73 use std::io;
74
75 #[test]
76 fn test_read_key_error_display() {
77 let err = SshError::ReadKey {
78 path: PathBuf::from("/path/to/key"),
79 source: io::Error::new(io::ErrorKind::NotFound, "file not found"),
80 };
81 let display = format!("{}", err);
82 assert!(display.contains("failed to read SSH key"));
83 assert!(display.contains("/path/to/key"));
84 }
85
86 #[test]
87 fn test_parse_key_error_display() {
88 let err = SshError::ParseKey {
90 path: PathBuf::from("/path/to/invalid_key"),
91 source: ssh_key::Error::AlgorithmUnknown,
92 };
93 let display = format!("{}", err);
94 assert!(display.contains("failed to parse SSH key"));
95 assert!(display.contains("/path/to/invalid_key"));
96 }
97
98 #[test]
99 fn test_generate_key_error_display() {
100 let err = SshError::GenerateKey(ssh_key::Error::AlgorithmUnknown);
101 let display = format!("{}", err);
102 assert!(display.contains("failed to generate SSH key"));
103 }
104
105 #[test]
106 fn test_serialize_key_error_display() {
107 let err = SshError::SerializeKey(ssh_key::Error::AlgorithmUnknown);
108 let display = format!("{}", err);
109 assert!(display.contains("failed to serialize key"));
110 }
111
112 #[test]
113 fn test_write_key_error_display() {
114 let err = SshError::WriteKey {
115 path: PathBuf::from("/path/to/key"),
116 source: io::Error::new(io::ErrorKind::PermissionDenied, "access denied"),
117 };
118 let display = format!("{}", err);
119 assert!(display.contains("failed to write key"));
120 assert!(display.contains("/path/to/key"));
121 }
122
123 #[test]
124 fn test_create_directory_error_display() {
125 let err = SshError::CreateDirectory {
126 path: PathBuf::from("/path/to/dir"),
127 source: io::Error::new(io::ErrorKind::PermissionDenied, "access denied"),
128 };
129 let display = format!("{}", err);
130 assert!(display.contains("failed to create directory"));
131 assert!(display.contains("/path/to/dir"));
132 }
133
134 #[test]
135 fn test_set_permissions_error_display() {
136 let err = SshError::SetPermissions {
137 path: PathBuf::from("/path/to/key"),
138 source: io::Error::new(io::ErrorKind::PermissionDenied, "access denied"),
139 };
140 let display = format!("{}", err);
141 assert!(display.contains("failed to set permissions"));
142 assert!(display.contains("/path/to/key"));
143 }
144
145 #[test]
146 fn test_unsupported_key_type_error_display() {
147 let err = SshError::UnsupportedKeyType("rsa".to_string());
148 let display = format!("{}", err);
149 assert!(display.contains("unsupported key type"));
150 assert!(display.contains("rsa"));
151 assert!(display.contains("only ed25519 is supported"));
152 }
153
154 #[test]
155 fn test_invalid_metadata_error_display() {
156 let err = SshError::InvalidMetadata {
157 field: "x-ssh-pubkey".to_string(),
158 message: "invalid header value".to_string(),
159 };
160 let display = format!("{}", err);
161 assert!(display.contains("invalid metadata value"));
162 assert!(display.contains("x-ssh-pubkey"));
163 assert!(display.contains("invalid header value"));
164 }
165
166 #[test]
167 fn test_error_debug() {
168 let err = SshError::UnsupportedKeyType("test".to_string());
169 let debug_str = format!("{:?}", err);
170 assert!(debug_str.contains("UnsupportedKeyType"));
171 assert!(debug_str.contains("test"));
172 }
173
174 #[test]
175 fn test_error_source_read_key() {
176 use std::error::Error;
177
178 let io_err = io::Error::new(io::ErrorKind::NotFound, "not found");
179 let err = SshError::ReadKey {
180 path: PathBuf::from("/path"),
181 source: io_err,
182 };
183
184 assert!(err.source().is_some());
186 }
187
188 #[test]
189 fn test_error_source_write_key() {
190 use std::error::Error;
191
192 let io_err = io::Error::new(io::ErrorKind::PermissionDenied, "denied");
193 let err = SshError::WriteKey {
194 path: PathBuf::from("/path"),
195 source: io_err,
196 };
197
198 assert!(err.source().is_some());
199 }
200
201 #[test]
202 fn test_error_source_create_directory() {
203 use std::error::Error;
204
205 let io_err = io::Error::new(io::ErrorKind::PermissionDenied, "denied");
206 let err = SshError::CreateDirectory {
207 path: PathBuf::from("/path"),
208 source: io_err,
209 };
210
211 assert!(err.source().is_some());
212 }
213
214 #[test]
215 fn test_error_source_set_permissions() {
216 use std::error::Error;
217
218 let io_err = io::Error::other("error");
219 let err = SshError::SetPermissions {
220 path: PathBuf::from("/path"),
221 source: io_err,
222 };
223
224 assert!(err.source().is_some());
225 }
226
227 #[test]
228 fn test_error_source_generate_key() {
229 use std::error::Error;
230
231 let err = SshError::GenerateKey(ssh_key::Error::AlgorithmUnknown);
232 assert!(err.source().is_some());
233 }
234
235 #[test]
236 fn test_error_source_serialize_key() {
237 use std::error::Error;
238
239 let err = SshError::SerializeKey(ssh_key::Error::AlgorithmUnknown);
240 assert!(err.source().is_some());
241 }
242
243 #[test]
244 fn test_error_source_parse_key() {
245 use std::error::Error;
246
247 let err = SshError::ParseKey {
248 path: PathBuf::from("/path"),
249 source: ssh_key::Error::AlgorithmUnknown,
250 };
251 assert!(err.source().is_some());
252 }
253
254 #[test]
255 fn test_error_no_source_unsupported_key_type() {
256 use std::error::Error;
257
258 let err = SshError::UnsupportedKeyType("rsa".to_string());
259 assert!(err.source().is_none());
261 }
262
263 #[test]
264 fn test_error_no_source_invalid_metadata() {
265 use std::error::Error;
266
267 let err = SshError::InvalidMetadata {
268 field: "x-ssh-pubkey".to_string(),
269 message: "invalid".to_string(),
270 };
271 assert!(err.source().is_none());
273 }
274}