age_setup/public_key.rs
1//! Age public key type.
2//!
3//! This module provides the [`PublicKey`] type, a validated wrapper around a
4//! string that is guaranteed to start with the age public key prefix `"age1"`.
5//! The type is the public half of an [`KeyPair`](crate::KeyPair) and can be
6//! freely shared, displayed, and cloned.
7
8use crate::errors::Result;
9use std::fmt;
10
11/// A validated age public key.
12///
13/// `PublicKey` is a thin wrapper around a [`String`] that ensures the contained
14/// key starts with `"age1"`. The validation is performed at construction time
15/// via [`new`](PublicKey::new), which delegates to
16/// [`validate_age_prefix`](crate::validation::validate_age_prefix).
17///
18/// # Security
19///
20/// While this type guarantees the `"age1"` prefix, it does **not** perform
21/// full Bech32 decoding or curve validation. The actual cryptographic checks
22/// are left to the `age` crate when the key is used for encryption.
23///
24/// # Examples
25///
26/// ```rust
27/// use age_setup::PublicKey;
28///
29/// let pk = PublicKey::new("age1mykey".into())?;
30/// println!("Public key: {}", pk); // uses Display
31/// println!("Exposed value: {}", pk.expose());
32/// # Ok::<(), age_setup::Error>(())
33/// ```
34#[derive(Debug, Clone)]
35pub struct PublicKey(String);
36
37impl PublicKey {
38 /// Creates a new `PublicKey` after validating the raw string.
39 ///
40 /// The string must be non‑empty and start with `"age1"` (case‑sensitive).
41 /// Validation is performed by [`validate_age_prefix`].
42 ///
43 /// # Errors
44 ///
45 /// Returns [`Error::Validation`](crate::Error::Validation) if the key
46 /// does not meet the prefix requirement.
47 ///
48 /// # Examples
49 ///
50 /// ```rust
51 /// # use age_setup::PublicKey;
52 /// let valid = PublicKey::new("age1abcdef".into()).unwrap();
53 /// assert_eq!(valid.expose(), "age1abcdef");
54 ///
55 /// let invalid = PublicKey::new("not-a-key".into());
56 /// assert!(invalid.is_err());
57 /// ```
58 pub fn new(raw: String) -> Result<Self> {
59 crate::validation::validate_age_prefix(&raw)?;
60 Ok(Self(raw))
61 }
62
63 /// Returns the raw string representation of the public key.
64 ///
65 /// The returned `&str` is safe to display, share, or use as an age
66 /// recipient.
67 ///
68 /// # Examples
69 ///
70 /// ```rust
71 /// # use age_setup::PublicKey;
72 /// let pk = PublicKey::new("age1test".into()).unwrap();
73 /// assert_eq!(pk.expose(), "age1test");
74 /// ```
75 #[must_use]
76 pub fn expose(&self) -> &str {
77 &self.0
78 }
79}
80
81/// The public key is printed in the format `age1...`.
82impl fmt::Display for PublicKey {
83 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
84 write!(f, "{}", self.0)
85 }
86}
87
88/// Allows `PublicKey` to be used where a `&str` is expected.
89///
90/// ```rust
91/// # use age_setup::PublicKey;
92/// fn print_key(key: &impl AsRef<str>) {
93/// println!("{}", key.as_ref());
94/// }
95/// let pk = PublicKey::new("age1foo".into()).unwrap();
96/// print_key(&pk);
97/// ```
98impl AsRef<str> for PublicKey {
99 fn as_ref(&self) -> &str {
100 &self.0
101 }
102}
103
104#[cfg(test)]
105mod tests {
106 use super::*;
107
108 #[test]
109 fn valid() {
110 let pk = PublicKey::new("age1test".into()).unwrap();
111 assert_eq!(pk.expose(), "age1test");
112 }
113
114 #[test]
115 fn invalid() {
116 assert!(PublicKey::new("bad".into()).is_err());
117 }
118}