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