1use std::fmt;
7use std::str::FromStr;
8
9#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
11pub enum Platform {
12 Pinotage,
14 Merlot,
16 Concord,
18 Vidal,
20 Zinfandel,
22 Syrah,
24}
25
26impl Platform {
27 pub fn base_url(&self) -> &'static str {
29 match self {
30 Platform::Pinotage => "https://pinotage-api.centrastage.net/api",
31 Platform::Merlot => "https://merlot-api.centrastage.net/api",
32 Platform::Concord => "https://concord-api.centrastage.net/api",
33 Platform::Vidal => "https://vidal-api.centrastage.net/api",
34 Platform::Zinfandel => "https://zinfandel-api.centrastage.net/api",
35 Platform::Syrah => "https://syrah-api.centrastage.net/api",
36 }
37 }
38
39 pub fn token_endpoint(&self) -> String {
41 format!("{}/public/oauth/token", self.base_url())
42 }
43
44 pub fn all() -> &'static [Platform] {
46 &[
47 Platform::Pinotage,
48 Platform::Merlot,
49 Platform::Concord,
50 Platform::Vidal,
51 Platform::Zinfandel,
52 Platform::Syrah,
53 ]
54 }
55}
56
57impl fmt::Display for Platform {
58 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
59 match self {
60 Platform::Pinotage => write!(f, "pinotage"),
61 Platform::Merlot => write!(f, "merlot"),
62 Platform::Concord => write!(f, "concord"),
63 Platform::Vidal => write!(f, "vidal"),
64 Platform::Zinfandel => write!(f, "zinfandel"),
65 Platform::Syrah => write!(f, "syrah"),
66 }
67 }
68}
69
70impl FromStr for Platform {
71 type Err = PlatformParseError;
72
73 fn from_str(s: &str) -> Result<Self, Self::Err> {
74 match s.to_lowercase().as_str() {
75 "pinotage" => Ok(Platform::Pinotage),
76 "merlot" => Ok(Platform::Merlot),
77 "concord" => Ok(Platform::Concord),
78 "vidal" => Ok(Platform::Vidal),
79 "zinfandel" => Ok(Platform::Zinfandel),
80 "syrah" => Ok(Platform::Syrah),
81 _ => Err(PlatformParseError(s.to_string())),
82 }
83 }
84}
85
86#[derive(Debug, Clone)]
88pub struct PlatformParseError(String);
89
90impl fmt::Display for PlatformParseError {
91 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
92 write!(
93 f,
94 "unknown platform '{}'. Valid platforms: pinotage, merlot, concord, vidal, zinfandel, syrah",
95 self.0
96 )
97 }
98}
99
100impl std::error::Error for PlatformParseError {}
101
102#[cfg(test)]
103mod tests {
104 use super::*;
105
106 #[test]
107 fn test_all_platform_urls() {
108 assert_eq!(
109 Platform::Pinotage.base_url(),
110 "https://pinotage-api.centrastage.net/api"
111 );
112 assert_eq!(
113 Platform::Merlot.base_url(),
114 "https://merlot-api.centrastage.net/api"
115 );
116 assert_eq!(
117 Platform::Concord.base_url(),
118 "https://concord-api.centrastage.net/api"
119 );
120 assert_eq!(
121 Platform::Vidal.base_url(),
122 "https://vidal-api.centrastage.net/api"
123 );
124 assert_eq!(
125 Platform::Zinfandel.base_url(),
126 "https://zinfandel-api.centrastage.net/api"
127 );
128 assert_eq!(
129 Platform::Syrah.base_url(),
130 "https://syrah-api.centrastage.net/api"
131 );
132 }
133
134 #[test]
135 fn test_token_endpoints() {
136 assert_eq!(
137 Platform::Merlot.token_endpoint(),
138 "https://merlot-api.centrastage.net/api/public/oauth/token"
139 );
140 assert_eq!(
141 Platform::Pinotage.token_endpoint(),
142 "https://pinotage-api.centrastage.net/api/public/oauth/token"
143 );
144 }
145
146 #[test]
147 fn test_platform_from_str() {
148 assert_eq!(Platform::from_str("merlot").unwrap(), Platform::Merlot);
149 assert_eq!(Platform::from_str("MERLOT").unwrap(), Platform::Merlot);
150 assert_eq!(Platform::from_str("Merlot").unwrap(), Platform::Merlot);
151 assert_eq!(Platform::from_str("pinotage").unwrap(), Platform::Pinotage);
152 assert_eq!(Platform::from_str("concord").unwrap(), Platform::Concord);
153 assert_eq!(Platform::from_str("vidal").unwrap(), Platform::Vidal);
154 assert_eq!(Platform::from_str("zinfandel").unwrap(), Platform::Zinfandel);
155 assert_eq!(Platform::from_str("syrah").unwrap(), Platform::Syrah);
156 }
157
158 #[test]
159 fn test_platform_from_str_invalid() {
160 let err = Platform::from_str("invalid").unwrap_err();
161 assert!(err.to_string().contains("unknown platform 'invalid'"));
162 assert!(err.to_string().contains("Valid platforms:"));
163 }
164
165 #[test]
166 fn test_platform_display() {
167 assert_eq!(Platform::Pinotage.to_string(), "pinotage");
168 assert_eq!(Platform::Merlot.to_string(), "merlot");
169 assert_eq!(Platform::Concord.to_string(), "concord");
170 assert_eq!(Platform::Vidal.to_string(), "vidal");
171 assert_eq!(Platform::Zinfandel.to_string(), "zinfandel");
172 assert_eq!(Platform::Syrah.to_string(), "syrah");
173 }
174
175 #[test]
176 fn test_platform_all() {
177 let all = Platform::all();
178 assert_eq!(all.len(), 6);
179 assert!(all.contains(&Platform::Pinotage));
180 assert!(all.contains(&Platform::Merlot));
181 assert!(all.contains(&Platform::Concord));
182 assert!(all.contains(&Platform::Vidal));
183 assert!(all.contains(&Platform::Zinfandel));
184 assert!(all.contains(&Platform::Syrah));
185 }
186
187 #[test]
188 fn test_platform_equality() {
189 assert_eq!(Platform::Merlot, Platform::Merlot);
190 assert_ne!(Platform::Merlot, Platform::Pinotage);
191 }
192
193 #[test]
194 fn test_platform_clone() {
195 let p1 = Platform::Merlot;
196 let p2 = p1;
197 assert_eq!(p1, p2);
198 }
199
200 #[test]
201 fn test_platform_hash() {
202 use std::collections::HashSet;
203 let mut set = HashSet::new();
204 set.insert(Platform::Merlot);
205 set.insert(Platform::Merlot); set.insert(Platform::Pinotage);
207 assert_eq!(set.len(), 2);
208 }
209}