1#[derive(Debug, Clone, PartialEq, Eq, Hash)]
6#[non_exhaustive]
7pub enum Principal {
8 Anonymous,
10 Signer {
12 ed25519: [u8; 32],
14 },
15 BearerHolder,
18 TransportPeer {
20 ed25519: [u8; 32],
22 },
23 SshForcedCommand {
27 key: Option<[u8; 32]>,
29 },
30}
31
32impl Principal {
33 #[must_use]
37 pub const fn kind(&self) -> &'static str {
38 match self {
39 Self::Anonymous => "anonymous",
40 Self::Signer { .. } => "signer",
41 Self::BearerHolder => "bearer",
42 Self::TransportPeer { .. } => "transport_peer",
43 Self::SshForcedCommand { .. } => "ssh_forced_command",
44 }
45 }
46
47 #[must_use]
49 pub fn ed25519(&self) -> Option<&[u8; 32]> {
50 match self {
51 Self::Signer { ed25519 } | Self::TransportPeer { ed25519 } => Some(ed25519),
52 Self::SshForcedCommand { key } => key.as_ref(),
53 Self::Anonymous | Self::BearerHolder => None,
54 }
55 }
56}
57
58#[cfg(test)]
59mod tests {
60 use super::*;
61
62 #[test]
63 fn ed25519_accessor_covers_ssh_key_some_and_none() {
64 let key = [7u8; 32];
65 assert_eq!(Principal::Signer { ed25519: key }.ed25519(), Some(&key));
66 assert_eq!(
67 Principal::TransportPeer { ed25519: key }.ed25519(),
68 Some(&key)
69 );
70 assert_eq!(
71 Principal::SshForcedCommand { key: Some(key) }.ed25519(),
72 Some(&key)
73 );
74 assert_eq!(Principal::SshForcedCommand { key: None }.ed25519(), None);
75 assert_eq!(Principal::Anonymous.ed25519(), None);
76 assert_eq!(Principal::BearerHolder.ed25519(), None);
77 }
78
79 #[test]
80 fn kind_labels_carry_no_key_material() {
81 let key = [7u8; 32];
82 let cases = [
83 (Principal::Anonymous, "anonymous"),
84 (Principal::Signer { ed25519: key }, "signer"),
85 (Principal::BearerHolder, "bearer"),
86 (Principal::TransportPeer { ed25519: key }, "transport_peer"),
87 (
88 Principal::SshForcedCommand { key: Some(key) },
89 "ssh_forced_command",
90 ),
91 ];
92 for (principal, kind) in cases {
93 assert_eq!(principal.kind(), kind);
94 }
95 }
96}