age_setup/public_key.rs
1use crate::errors::Result;
2use std::fmt;
3
4/// A validated age public key.
5///
6/// Wraps a string that is guaranteed to start with `"age1"` (the standard age
7/// recipient prefix). Construction fails if the provided string does not meet
8/// this requirement.
9///
10/// # Invariants
11///
12/// * The inner string is never empty.
13/// * The inner string always starts with `"age1"`.
14///
15/// # Examples
16///
17/// ```rust
18/// use age_setup::PublicKey;
19///
20/// let pk = PublicKey::new("age1abcdef".into())?;
21/// assert_eq!(pk.expose(), "age1abcdef");
22/// # Ok::<(), age_setup::Error>(())
23/// ```
24///
25/// Invalid input:
26///
27/// ```compile_fail
28/// use age_setup::PublicKey;
29///
30/// // This will not compile because `new` returns a Result.
31/// let pk: PublicKey = PublicKey::new("bad".into());
32/// ```
33#[derive(Debug, Clone)]
34pub struct PublicKey(String);
35
36impl PublicKey {
37 /// Creates a new `PublicKey` after validating the age prefix.
38 ///
39 /// The provided `raw` string must start with `"age1"` and must not be empty.
40 ///
41 /// # Errors
42 ///
43 /// Returns [`Error::Validation`](crate::Error::Validation) with
44 /// [`ValidationError::InvalidPublicKeyFormat`](crate::ValidationError::InvalidPublicKeyFormat)
45 /// if the key is empty or does not start with `"age1"`.
46 ///
47 /// # Examples
48 ///
49 /// ```rust
50 /// use age_setup::PublicKey;
51 ///
52 /// assert!(PublicKey::new("age1valid".into()).is_ok());
53 /// assert!(PublicKey::new("invalid".into()).is_err());
54 /// assert!(PublicKey::new("".into()).is_err());
55 /// ```
56 pub fn new(raw: String) -> Result<Self> {
57 crate::validation::validate_age_prefix(&raw)?;
58 Ok(Self(raw))
59 }
60
61 /// Returns a reference to the underlying public key string.
62 ///
63 /// This intentionally does **not** implement [`AsRef<str>`] directly
64 /// (though it is provided via a separate impl) to discourage accidental
65 /// logging. Use this method explicitly when you need the raw value.
66 ///
67 /// # Examples
68 ///
69 /// ```rust
70 /// use age_setup::PublicKey;
71 ///
72 /// let pk = PublicKey::new("age1secret".into())?;
73 /// assert_eq!(pk.expose(), "age1secret");
74 /// # Ok::<(), age_setup::Error>(())
75 /// ```
76 #[must_use]
77 pub fn expose(&self) -> &str {
78 &self.0
79 }
80}
81
82impl fmt::Display for PublicKey {
83 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
84 write!(f, "{}", self.0)
85 }
86}
87
88impl AsRef<str> for PublicKey {
89 fn as_ref(&self) -> &str {
90 &self.0
91 }
92}
93
94#[cfg(test)]
95mod tests {
96 use super::*;
97
98 #[test]
99 fn valid() {
100 let pk = PublicKey::new("age1test".into()).unwrap();
101 assert_eq!(pk.expose(), "age1test");
102 }
103
104 #[test]
105 fn invalid() {
106 assert!(PublicKey::new("bad".into()).is_err());
107 }
108}