age_setup/errors.rs
1//! Error types for the `age-setup` crate.
2//!
3//! This module defines the central [`Error`] enum that encompasses all possible
4//! failures in key generation and validation. It is designed to provide clear,
5//! actionable error messages through the [`thiserror`] crate.
6
7use thiserror::Error;
8
9/// The main error type for the `age-setup` crate.
10///
11/// All fallible operations in the library return a [`Result<T>`] where the error
12/// is of this type. It is deliberately kept small and covers only the failure
13/// modes that can occur:
14///
15/// - **Generation failures** – when the underlying `age` library fails to create
16/// a new identity.
17/// - **Validation failures** – when a public or secret key does not meet the
18/// expected format.
19///
20/// # Example
21///
22/// ```rust
23/// use age_setup::Error;
24///
25/// fn handle_error(e: Error) {
26/// match e {
27/// Error::Generation(err) => eprintln!("Generation error: {}", err),
28/// Error::Validation(err) => eprintln!("Validation error: {}", err),
29/// }
30/// }
31/// ```
32#[derive(Debug, Error)]
33pub enum Error {
34 /// An error that occurred during key generation.
35 ///
36 /// This variant wraps [`GenerationError`] and is produced by
37 /// [`build_keypair`](crate::build_keypair) if the internal identity generation
38 /// fails.
39 #[error("Key generation failed: {0}")]
40 Generation(#[from] GenerationError),
41
42 /// An error that occurred while validating a public or secret key.
43 ///
44 /// This variant wraps [`ValidationError`] and is produced by constructors like
45 /// [`PublicKey::new`](crate::PublicKey::new) or
46 /// [`SecretKey::new`](crate::SecretKey::new) when the provided string does
47 /// not conform to the expected format.
48 #[error("Validation failed: {0}")]
49 Validation(#[from] ValidationError),
50}
51
52/// The standard `Result` type used throughout the crate.
53///
54/// This is a convenience alias for `std::result::Result<T, Error>`.
55pub type Result<T> = std::result::Result<T, Error>;
56
57/// Errors that can occur during key generation.
58///
59/// Currently, the only possible failure is if the `age` library fails to create
60/// a new X25519 identity. This should be extremely rare and typically indicates
61/// a system-level issue (e.g., lack of entropy).
62#[derive(Debug, Error)]
63pub enum GenerationError {
64 /// The identity could not be created.
65 ///
66 /// This error is returned when the underlying `age` crate encounters an
67 /// internal failure while generating a new X25519 keypair.
68 #[error("Identity generation failed")]
69 IdentityCreationFailed,
70}
71
72/// Errors that can occur while validating a public or secret key.
73///
74/// Both public and secret keys are validated for basic format correctness before
75/// being wrapped. This enum captures the specific reason for the validation
76/// failure.
77#[derive(Debug, Error)]
78pub enum ValidationError {
79 /// The provided public key string is not in a valid format.
80 ///
81 /// This error is returned when a public key is empty or does not start with
82 /// the `"age1"` prefix.
83 #[error("Invalid public key format: {reason}")]
84 InvalidPublicKeyFormat { reason: String },
85
86 /// The provided secret key string is not in a valid format.
87 ///
88 /// This error is returned when a secret key is empty or does not start with
89 /// `"AGE-SECRET-KEY-1"`.
90 #[error("Invalid secret key format: {reason}")]
91 InvalidSecretKeyFormat { reason: String },
92}
93
94impl ValidationError {
95 /// Creates a new [`ValidationError::InvalidPublicKeyFormat`] with the given reason.
96 ///
97 /// This is a convenience constructor used internally by the public key validation
98 /// logic.
99 pub(crate) fn invalid_public_key(reason: impl Into<String>) -> Self {
100 Self::InvalidPublicKeyFormat {
101 reason: reason.into(),
102 }
103 }
104
105 /// Creates a new [`ValidationError::InvalidSecretKeyFormat`] with the given reason.
106 ///
107 /// This is a convenience constructor used internally by the secret key validation
108 /// logic.
109 pub(crate) fn invalid_secret_key(reason: impl Into<String>) -> Self {
110 Self::InvalidSecretKeyFormat {
111 reason: reason.into(),
112 }
113 }
114}
115
116#[cfg(test)]
117mod tests {
118 use super::*;
119
120 /// Ensure `GenerationError` can be converted into `Error`.
121 #[test]
122 fn generation_error_conversion() {
123 let gen_err = GenerationError::IdentityCreationFailed;
124 let err: Error = gen_err.into();
125 assert!(matches!(err, Error::Generation(_)));
126 }
127
128 /// Ensure `ValidationError` can be converted into `Error`.
129 #[test]
130 fn validation_error_conversion() {
131 let val_err = ValidationError::invalid_public_key("test");
132 let err: Error = val_err.into();
133 assert!(matches!(err, Error::Validation(_)));
134 }
135
136 /// Test the `Display` implementation of `GenerationError`.
137 #[test]
138 fn generation_error_display() {
139 let e = GenerationError::IdentityCreationFailed;
140 assert_eq!(format!("{}", e), "Identity generation failed");
141 }
142
143 /// Test the `Display` implementation of `ValidationError` for public key.
144 #[test]
145 fn validation_error_display_public() {
146 let e = ValidationError::invalid_public_key("too short");
147 assert_eq!(format!("{}", e), "Invalid public key format: too short");
148 }
149
150 /// Test the `Display` implementation of `ValidationError` for secret key.
151 #[test]
152 fn validation_error_display_secret() {
153 let e = ValidationError::invalid_secret_key("empty");
154 assert_eq!(format!("{}", e), "Invalid secret key format: empty");
155 }
156
157 /// Test that the `Result` alias works properly.
158 #[test]
159 fn result_type_alias() {
160 fn returns_ok() -> Result<()> {
161 Ok(())
162 }
163 fn returns_err() -> Result<()> {
164 Err(Error::Generation(GenerationError::IdentityCreationFailed))
165 }
166 assert!(returns_ok().is_ok());
167 assert!(returns_err().is_err());
168 }
169}