1use std::{fmt, str::FromStr};
6
7use uuid::Uuid;
8
9use crate::{OdosError, Result};
10
11#[derive(Clone, Copy, PartialEq, Eq)]
28pub struct ApiKey(Uuid);
29
30impl ApiKey {
31 pub fn new(uuid: Uuid) -> Self {
48 Self(uuid)
49 }
50
51 pub fn as_uuid(&self) -> &Uuid {
58 &self.0
59 }
60
61 pub fn as_str(&self) -> String {
68 self.0.to_string()
69 }
70}
71
72impl FromStr for ApiKey {
73 type Err = OdosError;
74
75 fn from_str(s: &str) -> Result<Self> {
76 let uuid = Uuid::from_str(s).map_err(|e| {
77 OdosError::invalid_input(format!("Invalid API key format (expected UUID): {}", e))
78 })?;
79 Ok(Self(uuid))
80 }
81}
82
83impl From<Uuid> for ApiKey {
84 fn from(uuid: Uuid) -> Self {
85 Self(uuid)
86 }
87}
88
89impl fmt::Debug for ApiKey {
91 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
92 f.write_str("ApiKey([REDACTED])")
93 }
94}
95
96impl fmt::Display for ApiKey {
98 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
99 f.write_str("[REDACTED]")
100 }
101}
102
103#[cfg(test)]
104mod tests {
105 use super::*;
106
107 #[test]
108 fn test_api_key_new() {
109 let uuid = Uuid::new_v4();
110 let api_key = ApiKey::new(uuid);
111 assert_eq!(api_key.as_uuid(), &uuid);
112 }
113
114 #[test]
115 fn test_api_key_from_uuid() {
116 let uuid = Uuid::new_v4();
117 let api_key = ApiKey::from(uuid);
118 assert_eq!(api_key.as_uuid(), &uuid);
119 }
120
121 #[test]
122 fn test_api_key_from_str() {
123 let uuid = Uuid::new_v4();
124 let key = uuid.to_string();
125 let api_key = ApiKey::from_str(&key).unwrap();
126 assert_eq!(api_key.as_str(), key);
127 }
128
129 #[test]
130 fn test_api_key_from_str_invalid() {
131 let result = ApiKey::from_str("not-a-uuid");
132 assert!(result.is_err());
133 if let Err(e) = result {
134 let error_msg = e.to_string();
135 assert!(error_msg.contains("Invalid API key format"));
136 }
137 }
138
139 #[test]
140 fn test_api_key_debug_redacted() {
141 let uuid = Uuid::new_v4();
142 let api_key = ApiKey::new(uuid);
143 let debug_output = format!("{:?}", api_key);
144 assert_eq!(debug_output, "ApiKey([REDACTED])");
145 let uuid_str = uuid.to_string();
146 assert!(!debug_output.contains(&uuid_str));
147 }
148
149 #[test]
150 fn test_api_key_display_redacted() {
151 let uuid = Uuid::new_v4();
152 let api_key = ApiKey::new(uuid);
153 let display_output = format!("{}", api_key);
154 assert_eq!(display_output, "[REDACTED]");
155 let uuid_str = uuid.to_string();
156 assert!(!display_output.contains(&uuid_str));
157 }
158}