1#[cfg(feature = "serde")]
2use serde::{Deserialize, Serialize};
3use std::str::{self, FromStr};
4use std::{env, fmt};
5
6use crate::error::RegionError;
7
8#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
32#[derive(Clone, Debug, Eq, PartialEq)]
33pub enum Region {
34 UsEast1,
36 UsEast2,
38 UsWest1,
40 UsWest2,
42 CaCentral1,
44 AfSouth1,
46 ApEast1,
48 ApSouth1,
50 ApNortheast1,
52 ApNortheast2,
54 ApNortheast3,
56 ApSoutheast1,
58 ApSoutheast2,
60 CnNorth1,
62 CnNorthwest1,
64 EuNorth1,
66 EuCentral1,
68 EuCentral2,
70 EuWest1,
72 EuWest2,
74 EuWest3,
76 IlCentral1,
78 MeSouth1,
80 SaEast1,
82 DoNyc3,
84 DoAms3,
86 DoSgp1,
88 DoFra1,
90 OvhGra,
92 OvhRbx,
94 OvhSbg,
96 OvhDe,
98 OvhUk,
100 OvhWaw,
102 OvhBhs,
104 OvhCaEastTor,
106 OvhSgp,
108 Yandex,
110 WaUsEast1,
112 WaUsEast2,
114 WaUsCentral1,
116 WaUsWest1,
118 WaCaCentral1,
120 WaEuCentral1,
122 WaEuCentral2,
124 WaEuWest1,
126 WaEuWest2,
128 WaApNortheast1,
130 WaApNortheast2,
132 WaApSoutheast1,
134 WaApSoutheast2,
136 R2 { account_id: String },
138 R2Eu { account_id: String },
140 Custom { region: String, endpoint: String },
142}
143
144impl fmt::Display for Region {
145 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
146 use self::Region::*;
147 match *self {
148 UsEast1 => write!(f, "us-east-1"),
149 UsEast2 => write!(f, "us-east-2"),
150 UsWest1 => write!(f, "us-west-1"),
151 UsWest2 => write!(f, "us-west-2"),
152 AfSouth1 => write!(f, "af-south-1"),
153 CaCentral1 => write!(f, "ca-central-1"),
154 ApEast1 => write!(f, "ap-east-1"),
155 ApSouth1 => write!(f, "ap-south-1"),
156 ApNortheast1 => write!(f, "ap-northeast-1"),
157 ApNortheast2 => write!(f, "ap-northeast-2"),
158 ApNortheast3 => write!(f, "ap-northeast-3"),
159 ApSoutheast1 => write!(f, "ap-southeast-1"),
160 ApSoutheast2 => write!(f, "ap-southeast-2"),
161 CnNorth1 => write!(f, "cn-north-1"),
162 CnNorthwest1 => write!(f, "cn-northwest-1"),
163 EuNorth1 => write!(f, "eu-north-1"),
164 EuCentral1 => write!(f, "eu-central-1"),
165 EuCentral2 => write!(f, "eu-central-2"),
166 EuWest1 => write!(f, "eu-west-1"),
167 EuWest2 => write!(f, "eu-west-2"),
168 EuWest3 => write!(f, "eu-west-3"),
169 SaEast1 => write!(f, "sa-east-1"),
170 IlCentral1 => write!(f, "il-central-1"),
171 MeSouth1 => write!(f, "me-south-1"),
172 DoNyc3 => write!(f, "nyc3"),
173 DoAms3 => write!(f, "ams3"),
174 DoSgp1 => write!(f, "sgp1"),
175 DoFra1 => write!(f, "fra1"),
176 Yandex => write!(f, "ru-central1"),
177 WaUsEast1 => write!(f, "us-east-1"),
178 WaUsEast2 => write!(f, "us-east-2"),
179 WaUsCentral1 => write!(f, "us-central-1"),
180 WaUsWest1 => write!(f, "us-west-1"),
181 WaCaCentral1 => write!(f, "ca-central-1"),
182 WaEuCentral1 => write!(f, "eu-central-1"),
183 WaEuCentral2 => write!(f, "eu-central-2"),
184 WaEuWest1 => write!(f, "eu-west-1"),
185 WaEuWest2 => write!(f, "eu-west-2"),
186 WaApNortheast1 => write!(f, "ap-northeast-1"),
187 WaApNortheast2 => write!(f, "ap-northeast-2"),
188 WaApSoutheast1 => write!(f, "ap-southeast-1"),
189 WaApSoutheast2 => write!(f, "ap-southeast-2"),
190 OvhGra => write!(f, "gra"),
191 OvhRbx => write!(f, "rbx"),
192 OvhSbg => write!(f, "sbg"),
193 OvhDe => write!(f, "de"),
194 OvhUk => write!(f, "uk"),
195 OvhWaw => write!(f, "waw"),
196 OvhBhs => write!(f, "bhs"),
197 OvhCaEastTor => write!(f, "ca-east-tor"),
198 OvhSgp => write!(f, "sgp"),
199 R2 { .. } => write!(f, "auto"),
200 R2Eu { .. } => write!(f, "auto"),
201 Custom { ref region, .. } => write!(f, "{}", region),
202 }
203 }
204}
205
206impl FromStr for Region {
207 type Err = std::str::Utf8Error;
208
209 fn from_str(s: &str) -> Result<Self, Self::Err> {
210 use self::Region::*;
211 match s {
212 "us-east-1" => Ok(UsEast1),
213 "us-east-2" => Ok(UsEast2),
214 "us-west-1" => Ok(UsWest1),
215 "us-west-2" => Ok(UsWest2),
216 "ca-central-1" => Ok(CaCentral1),
217 "af-south-1" => Ok(AfSouth1),
218 "ap-east-1" => Ok(ApEast1),
219 "ap-south-1" => Ok(ApSouth1),
220 "ap-northeast-1" => Ok(ApNortheast1),
221 "ap-northeast-2" => Ok(ApNortheast2),
222 "ap-northeast-3" => Ok(ApNortheast3),
223 "ap-southeast-1" => Ok(ApSoutheast1),
224 "ap-southeast-2" => Ok(ApSoutheast2),
225 "cn-north-1" => Ok(CnNorth1),
226 "cn-northwest-1" => Ok(CnNorthwest1),
227 "eu-north-1" => Ok(EuNorth1),
228 "eu-central-1" => Ok(EuCentral1),
229 "eu-central-2" => Ok(EuCentral2),
230 "eu-west-1" => Ok(EuWest1),
231 "eu-west-2" => Ok(EuWest2),
232 "eu-west-3" => Ok(EuWest3),
233 "sa-east-1" => Ok(SaEast1),
234 "il-central-1" => Ok(IlCentral1),
235 "me-south-1" => Ok(MeSouth1),
236 "nyc3" => Ok(DoNyc3),
237 "ams3" => Ok(DoAms3),
238 "sgp1" => Ok(DoSgp1),
239 "fra1" => Ok(DoFra1),
240 "yandex" => Ok(Yandex),
241 "ru-central1" => Ok(Yandex),
242 "wa-us-east-1" => Ok(WaUsEast1),
243 "wa-us-east-2" => Ok(WaUsEast2),
244 "wa-us-central-1" => Ok(WaUsCentral1),
245 "wa-us-west-1" => Ok(WaUsWest1),
246 "wa-ca-central-1" => Ok(WaCaCentral1),
247 "wa-eu-central-1" => Ok(WaEuCentral1),
248 "wa-eu-central-2" => Ok(WaEuCentral2),
249 "wa-eu-west-1" => Ok(WaEuWest1),
250 "wa-eu-west-2" => Ok(WaEuWest2),
251 "wa-ap-northeast-1" => Ok(WaApNortheast1),
252 "wa-ap-northeast-2" => Ok(WaApNortheast2),
253 "wa-ap-southeast-1" => Ok(WaApSoutheast1),
254 "wa-ap-southeast-2" => Ok(WaApSoutheast2),
255 x => Ok(Custom {
256 region: x.to_string(),
257 endpoint: x.to_string(),
258 }),
259 }
260 }
261}
262
263impl Region {
264 pub fn endpoint(&self) -> String {
265 use self::Region::*;
266 match *self {
267 UsEast1 => String::from("s3.amazonaws.com"),
270 UsEast2 => String::from("s3-us-east-2.amazonaws.com"),
271 UsWest1 => String::from("s3-us-west-1.amazonaws.com"),
272 UsWest2 => String::from("s3-us-west-2.amazonaws.com"),
273 CaCentral1 => String::from("s3-ca-central-1.amazonaws.com"),
274 AfSouth1 => String::from("s3-af-south-1.amazonaws.com"),
275 ApEast1 => String::from("s3-ap-east-1.amazonaws.com"),
276 ApSouth1 => String::from("s3-ap-south-1.amazonaws.com"),
277 ApNortheast1 => String::from("s3-ap-northeast-1.amazonaws.com"),
278 ApNortheast2 => String::from("s3-ap-northeast-2.amazonaws.com"),
279 ApNortheast3 => String::from("s3-ap-northeast-3.amazonaws.com"),
280 ApSoutheast1 => String::from("s3-ap-southeast-1.amazonaws.com"),
281 ApSoutheast2 => String::from("s3-ap-southeast-2.amazonaws.com"),
282 CnNorth1 => String::from("s3.cn-north-1.amazonaws.com.cn"),
283 CnNorthwest1 => String::from("s3.cn-northwest-1.amazonaws.com.cn"),
284 EuNorth1 => String::from("s3-eu-north-1.amazonaws.com"),
285 EuCentral1 => String::from("s3.eu-central-1.amazonaws.com"),
286 EuCentral2 => String::from("s3.eu-central-2.amazonaws.com"),
287 EuWest1 => String::from("s3-eu-west-1.amazonaws.com"),
288 EuWest2 => String::from("s3-eu-west-2.amazonaws.com"),
289 EuWest3 => String::from("s3-eu-west-3.amazonaws.com"),
290 SaEast1 => String::from("s3-sa-east-1.amazonaws.com"),
291 IlCentral1 => String::from("s3.il-central-1.amazonaws.com"),
292 MeSouth1 => String::from("s3-me-south-1.amazonaws.com"),
293 DoNyc3 => String::from("nyc3.digitaloceanspaces.com"),
294 DoAms3 => String::from("ams3.digitaloceanspaces.com"),
295 DoSgp1 => String::from("sgp1.digitaloceanspaces.com"),
296 DoFra1 => String::from("fra1.digitaloceanspaces.com"),
297 Yandex => String::from("storage.yandexcloud.net"),
298 WaUsEast1 => String::from("s3.us-east-1.wasabisys.com"),
299 WaUsEast2 => String::from("s3.us-east-2.wasabisys.com"),
300 WaUsCentral1 => String::from("s3.us-central-1.wasabisys.com"),
301 WaUsWest1 => String::from("s3.us-west-1.wasabisys.com"),
302 WaCaCentral1 => String::from("s3.ca-central-1.wasabisys.com"),
303 WaEuCentral1 => String::from("s3.eu-central-1.wasabisys.com"),
304 WaEuCentral2 => String::from("s3.eu-central-2.wasabisys.com"),
305 WaEuWest1 => String::from("s3.eu-west-1.wasabisys.com"),
306 WaEuWest2 => String::from("s3.eu-west-2.wasabisys.com"),
307 WaApNortheast1 => String::from("s3.ap-northeast-1.wasabisys.com"),
308 WaApNortheast2 => String::from("s3.ap-northeast-2.wasabisys.com"),
309 WaApSoutheast1 => String::from("s3.ap-southeast-1.wasabisys.com"),
310 WaApSoutheast2 => String::from("s3.ap-southeast-2.wasabisys.com"),
311 OvhGra => String::from("s3.gra.io.cloud.ovh.net"),
312 OvhRbx => String::from("s3.rbx.io.cloud.ovh.net"),
313 OvhSbg => String::from("s3.sbg.io.cloud.ovh.net"),
314 OvhDe => String::from("s3.de.io.cloud.ovh.net"),
315 OvhUk => String::from("s3.uk.io.cloud.ovh.net"),
316 OvhWaw => String::from("s3.waw.io.cloud.ovh.net"),
317 OvhBhs => String::from("s3.bhs.io.cloud.ovh.net"),
318 OvhCaEastTor => String::from("s3.ca-east-tor.io.cloud.ovh.net"),
319 OvhSgp => String::from("s3.sgp.io.cloud.ovh.net"),
320 R2 { ref account_id } => format!("{}.r2.cloudflarestorage.com", account_id),
321 R2Eu { ref account_id } => format!("{}.eu.r2.cloudflarestorage.com", account_id),
322 Custom { ref endpoint, .. } => endpoint.to_string(),
323 }
324 }
325
326 pub fn scheme(&self) -> String {
327 match *self {
328 Region::Custom { ref endpoint, .. } => match endpoint.find("://") {
329 Some(pos) => endpoint[..pos].to_string(),
330 None => "https".to_string(),
331 },
332 _ => "https".to_string(),
333 }
334 }
335
336 pub fn host(&self) -> String {
337 match *self {
338 Region::Custom { ref endpoint, .. } => {
339 let host = match endpoint.find("://") {
340 Some(pos) => endpoint[pos + 3..].to_string(),
341 None => endpoint.to_string(),
342 };
343 host.trim_end_matches('/').to_string()
346 }
347 _ => self.endpoint(),
348 }
349 }
350
351 pub fn from_env(region_env: &str, endpoint_env: Option<&str>) -> Result<Region, RegionError> {
352 if let Some(endpoint_env) = endpoint_env {
353 Ok(Region::Custom {
354 region: env::var(region_env)?,
355 endpoint: env::var(endpoint_env)?,
356 })
357 } else {
358 Ok(Region::from_str(&env::var(region_env)?)?)
359 }
360 }
361
362 pub fn from_default_env() -> Result<Region, RegionError> {
364 if let Ok(endpoint) = env::var("AWS_ENDPOINT") {
365 Ok(Region::Custom {
366 region: env::var("AWS_REGION")?,
367 endpoint,
368 })
369 } else {
370 Ok(Region::from_str(&env::var("AWS_REGION")?)?)
371 }
372 }
373}
374
375#[test]
376fn yandex_object_storage() {
377 let yandex = Region::Custom {
378 endpoint: "storage.yandexcloud.net".to_string(),
379 region: "ru-central1".to_string(),
380 };
381
382 let yandex_region = "ru-central1".parse::<Region>().unwrap();
383
384 assert_eq!(yandex.endpoint(), yandex_region.endpoint());
385
386 assert_eq!(yandex.to_string(), yandex_region.to_string());
387}
388
389#[test]
390fn test_region_eu_central_2() {
391 let region = "eu-central-2".parse::<Region>().unwrap();
392 assert_eq!(region.endpoint(), "s3.eu-central-2.amazonaws.com");
393}
394
395#[test]
396fn test_custom_endpoint_trailing_slash() {
397 let region_with_slash = Region::Custom {
399 region: "eu-central-1".to_owned(),
400 endpoint: "https://s3.gra.io.cloud.ovh.net/".to_owned(),
401 };
402 assert_eq!(region_with_slash.host(), "s3.gra.io.cloud.ovh.net");
403
404 let region_without_slash = Region::Custom {
406 region: "eu-central-1".to_owned(),
407 endpoint: "https://s3.gra.io.cloud.ovh.net".to_owned(),
408 };
409 assert_eq!(region_without_slash.host(), "s3.gra.io.cloud.ovh.net");
410
411 let region_multiple_slashes = Region::Custom {
413 region: "eu-central-1".to_owned(),
414 endpoint: "https://s3.example.com///".to_owned(),
415 };
416 assert_eq!(region_multiple_slashes.host(), "s3.example.com");
417
418 let region_with_port = Region::Custom {
420 region: "eu-central-1".to_owned(),
421 endpoint: "http://localhost:9000/".to_owned(),
422 };
423 assert_eq!(region_with_port.host(), "localhost:9000");
424}