Skip to main content

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}