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 CaWest1,
46 AfSouth1,
48 ApEast1,
50 ApEast2,
52 ApSouth1,
54 ApSouth2,
56 ApNortheast1,
58 ApNortheast2,
60 ApNortheast3,
62 ApSoutheast1,
64 ApSoutheast2,
66 ApSoutheast3,
68 ApSoutheast4,
70 ApSoutheast5,
72 ApSoutheast6,
74 ApSoutheast7,
76 CnNorth1,
78 CnNorthwest1,
80 EuNorth1,
82 EuCentral1,
84 EuCentral2,
86 EuSouth1,
88 EuSouth2,
90 EuWest1,
92 EuWest2,
94 EuWest3,
96 IlCentral1,
98 MeSouth1,
100 MeCentral1,
102 MxCentral1,
104 SaEast1,
106 DoNyc3,
108 DoAms3,
110 DoSgp1,
112 DoFra1,
114 OvhGra,
116 OvhRbx,
118 OvhSbg,
120 OvhDe,
122 OvhUk,
124 OvhWaw,
126 OvhBhs,
128 OvhCaEastTor,
130 OvhSgp,
132 Yandex,
134 WaUsEast1,
136 WaUsEast2,
138 WaUsCentral1,
140 WaUsWest1,
142 WaCaCentral1,
144 WaEuCentral1,
146 WaEuCentral2,
148 WaEuWest1,
150 WaEuWest2,
152 WaApNortheast1,
154 WaApNortheast2,
156 WaApSoutheast1,
158 WaApSoutheast2,
160 R2 { account_id: String },
162 R2Eu { account_id: String },
164 Custom { region: String, endpoint: String },
166}
167
168impl fmt::Display for Region {
169 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
170 use self::Region::*;
171 match *self {
172 UsEast1 => write!(f, "us-east-1"),
173 UsEast2 => write!(f, "us-east-2"),
174 UsWest1 => write!(f, "us-west-1"),
175 UsWest2 => write!(f, "us-west-2"),
176 AfSouth1 => write!(f, "af-south-1"),
177 CaCentral1 => write!(f, "ca-central-1"),
178 CaWest1 => write!(f, "ca-west-1"),
179 ApEast1 => write!(f, "ap-east-1"),
180 ApEast2 => write!(f, "ap-east-2"),
181 ApSouth1 => write!(f, "ap-south-1"),
182 ApSouth2 => write!(f, "ap-south-2"),
183 ApNortheast1 => write!(f, "ap-northeast-1"),
184 ApNortheast2 => write!(f, "ap-northeast-2"),
185 ApNortheast3 => write!(f, "ap-northeast-3"),
186 ApSoutheast1 => write!(f, "ap-southeast-1"),
187 ApSoutheast2 => write!(f, "ap-southeast-2"),
188 ApSoutheast3 => write!(f, "ap-southeast-3"),
189 ApSoutheast4 => write!(f, "ap-southeast-4"),
190 ApSoutheast5 => write!(f, "ap-southeast-5"),
191 ApSoutheast6 => write!(f, "ap-southeast-6"),
192 ApSoutheast7 => write!(f, "ap-southeast-7"),
193 CnNorth1 => write!(f, "cn-north-1"),
194 CnNorthwest1 => write!(f, "cn-northwest-1"),
195 EuNorth1 => write!(f, "eu-north-1"),
196 EuCentral1 => write!(f, "eu-central-1"),
197 EuCentral2 => write!(f, "eu-central-2"),
198 EuSouth1 => write!(f, "eu-south-1"),
199 EuSouth2 => write!(f, "eu-south-2"),
200 EuWest1 => write!(f, "eu-west-1"),
201 EuWest2 => write!(f, "eu-west-2"),
202 EuWest3 => write!(f, "eu-west-3"),
203 SaEast1 => write!(f, "sa-east-1"),
204 IlCentral1 => write!(f, "il-central-1"),
205 MeCentral1 => write!(f, "me-central-1"),
206 MeSouth1 => write!(f, "me-south-1"),
207 MxCentral1 => write!(f, "mx-central-1"),
208 DoNyc3 => write!(f, "nyc3"),
209 DoAms3 => write!(f, "ams3"),
210 DoSgp1 => write!(f, "sgp1"),
211 DoFra1 => write!(f, "fra1"),
212 Yandex => write!(f, "ru-central1"),
213 WaUsEast1 => write!(f, "us-east-1"),
214 WaUsEast2 => write!(f, "us-east-2"),
215 WaUsCentral1 => write!(f, "us-central-1"),
216 WaUsWest1 => write!(f, "us-west-1"),
217 WaCaCentral1 => write!(f, "ca-central-1"),
218 WaEuCentral1 => write!(f, "eu-central-1"),
219 WaEuCentral2 => write!(f, "eu-central-2"),
220 WaEuWest1 => write!(f, "eu-west-1"),
221 WaEuWest2 => write!(f, "eu-west-2"),
222 WaApNortheast1 => write!(f, "ap-northeast-1"),
223 WaApNortheast2 => write!(f, "ap-northeast-2"),
224 WaApSoutheast1 => write!(f, "ap-southeast-1"),
225 WaApSoutheast2 => write!(f, "ap-southeast-2"),
226 OvhGra => write!(f, "gra"),
227 OvhRbx => write!(f, "rbx"),
228 OvhSbg => write!(f, "sbg"),
229 OvhDe => write!(f, "de"),
230 OvhUk => write!(f, "uk"),
231 OvhWaw => write!(f, "waw"),
232 OvhBhs => write!(f, "bhs"),
233 OvhCaEastTor => write!(f, "ca-east-tor"),
234 OvhSgp => write!(f, "sgp"),
235 R2 { .. } => write!(f, "auto"),
236 R2Eu { .. } => write!(f, "auto"),
237 Custom { ref region, .. } => write!(f, "{}", region),
238 }
239 }
240}
241
242impl FromStr for Region {
243 type Err = std::str::Utf8Error;
244
245 fn from_str(s: &str) -> Result<Self, Self::Err> {
246 use self::Region::*;
247 match s {
248 "us-east-1" => Ok(UsEast1),
249 "us-east-2" => Ok(UsEast2),
250 "us-west-1" => Ok(UsWest1),
251 "us-west-2" => Ok(UsWest2),
252 "ca-central-1" => Ok(CaCentral1),
253 "ca-west-1" => Ok(CaWest1),
254 "af-south-1" => Ok(AfSouth1),
255 "ap-east-1" => Ok(ApEast1),
256 "ap-east-2" => Ok(ApEast2),
257 "ap-south-1" => Ok(ApSouth1),
258 "ap-south-2" => Ok(ApSouth2),
259 "ap-northeast-1" => Ok(ApNortheast1),
260 "ap-northeast-2" => Ok(ApNortheast2),
261 "ap-northeast-3" => Ok(ApNortheast3),
262 "ap-southeast-1" => Ok(ApSoutheast1),
263 "ap-southeast-2" => Ok(ApSoutheast2),
264 "ap-southeast-3" => Ok(ApSoutheast3),
265 "ap-southeast-4" => Ok(ApSoutheast4),
266 "ap-southeast-5" => Ok(ApSoutheast5),
267 "ap-southeast-6" => Ok(ApSoutheast6),
268 "ap-southeast-7" => Ok(ApSoutheast7),
269 "cn-north-1" => Ok(CnNorth1),
270 "cn-northwest-1" => Ok(CnNorthwest1),
271 "eu-north-1" => Ok(EuNorth1),
272 "eu-central-1" => Ok(EuCentral1),
273 "eu-central-2" => Ok(EuCentral2),
274 "eu-south-1" => Ok(EuSouth1),
275 "eu-south-2" => Ok(EuSouth2),
276 "eu-west-1" => Ok(EuWest1),
277 "eu-west-2" => Ok(EuWest2),
278 "eu-west-3" => Ok(EuWest3),
279 "sa-east-1" => Ok(SaEast1),
280 "il-central-1" => Ok(IlCentral1),
281 "me-central-1" => Ok(MeCentral1),
282 "me-south-1" => Ok(MeSouth1),
283 "mx-central-1" => Ok(MxCentral1),
284 "nyc3" => Ok(DoNyc3),
285 "ams3" => Ok(DoAms3),
286 "sgp1" => Ok(DoSgp1),
287 "fra1" => Ok(DoFra1),
288 "yandex" => Ok(Yandex),
289 "ru-central1" => Ok(Yandex),
290 "wa-us-east-1" => Ok(WaUsEast1),
291 "wa-us-east-2" => Ok(WaUsEast2),
292 "wa-us-central-1" => Ok(WaUsCentral1),
293 "wa-us-west-1" => Ok(WaUsWest1),
294 "wa-ca-central-1" => Ok(WaCaCentral1),
295 "wa-eu-central-1" => Ok(WaEuCentral1),
296 "wa-eu-central-2" => Ok(WaEuCentral2),
297 "wa-eu-west-1" => Ok(WaEuWest1),
298 "wa-eu-west-2" => Ok(WaEuWest2),
299 "wa-ap-northeast-1" => Ok(WaApNortheast1),
300 "wa-ap-northeast-2" => Ok(WaApNortheast2),
301 "wa-ap-southeast-1" => Ok(WaApSoutheast1),
302 "wa-ap-southeast-2" => Ok(WaApSoutheast2),
303 x => Ok(Custom {
304 region: x.to_string(),
305 endpoint: x.to_string(),
306 }),
307 }
308 }
309}
310
311impl Region {
312 pub fn endpoint(&self) -> String {
313 use self::Region::*;
314 match *self {
315 UsEast1 => String::from("s3.amazonaws.com"),
318 UsEast2 => String::from("s3-us-east-2.amazonaws.com"),
319 UsWest1 => String::from("s3-us-west-1.amazonaws.com"),
320 UsWest2 => String::from("s3-us-west-2.amazonaws.com"),
321 CaCentral1 => String::from("s3-ca-central-1.amazonaws.com"),
322 CaWest1 => String::from("s3.ca-west-1.amazonaws.com"),
323 AfSouth1 => String::from("s3-af-south-1.amazonaws.com"),
324 ApEast1 => String::from("s3-ap-east-1.amazonaws.com"),
325 ApEast2 => String::from("s3.ap-east-2.amazonaws.com"),
326 ApSouth1 => String::from("s3-ap-south-1.amazonaws.com"),
327 ApSouth2 => String::from("s3.ap-south-2.amazonaws.com"),
328 ApNortheast1 => String::from("s3-ap-northeast-1.amazonaws.com"),
329 ApNortheast2 => String::from("s3-ap-northeast-2.amazonaws.com"),
330 ApNortheast3 => String::from("s3-ap-northeast-3.amazonaws.com"),
331 ApSoutheast1 => String::from("s3-ap-southeast-1.amazonaws.com"),
332 ApSoutheast2 => String::from("s3-ap-southeast-2.amazonaws.com"),
333 ApSoutheast3 => String::from("s3.ap-southeast-3.amazonaws.com"),
334 ApSoutheast4 => String::from("s3.ap-southeast-4.amazonaws.com"),
335 ApSoutheast5 => String::from("s3.ap-southeast-5.amazonaws.com"),
336 ApSoutheast6 => String::from("s3.ap-southeast-6.amazonaws.com"),
337 ApSoutheast7 => String::from("s3.ap-southeast-7.amazonaws.com"),
338 CnNorth1 => String::from("s3.cn-north-1.amazonaws.com.cn"),
339 CnNorthwest1 => String::from("s3.cn-northwest-1.amazonaws.com.cn"),
340 EuNorth1 => String::from("s3-eu-north-1.amazonaws.com"),
341 EuCentral1 => String::from("s3.eu-central-1.amazonaws.com"),
342 EuCentral2 => String::from("s3.eu-central-2.amazonaws.com"),
343 EuSouth1 => String::from("s3.eu-south-1.amazonaws.com"),
344 EuSouth2 => String::from("s3.eu-south-2.amazonaws.com"),
345 EuWest1 => String::from("s3-eu-west-1.amazonaws.com"),
346 EuWest2 => String::from("s3-eu-west-2.amazonaws.com"),
347 EuWest3 => String::from("s3-eu-west-3.amazonaws.com"),
348 SaEast1 => String::from("s3-sa-east-1.amazonaws.com"),
349 IlCentral1 => String::from("s3.il-central-1.amazonaws.com"),
350 MeCentral1 => String::from("s3.me-central-1.amazonaws.com"),
351 MeSouth1 => String::from("s3-me-south-1.amazonaws.com"),
352 MxCentral1 => String::from("s3.mx-central-1.amazonaws.com"),
353 DoNyc3 => String::from("nyc3.digitaloceanspaces.com"),
354 DoAms3 => String::from("ams3.digitaloceanspaces.com"),
355 DoSgp1 => String::from("sgp1.digitaloceanspaces.com"),
356 DoFra1 => String::from("fra1.digitaloceanspaces.com"),
357 Yandex => String::from("storage.yandexcloud.net"),
358 WaUsEast1 => String::from("s3.us-east-1.wasabisys.com"),
359 WaUsEast2 => String::from("s3.us-east-2.wasabisys.com"),
360 WaUsCentral1 => String::from("s3.us-central-1.wasabisys.com"),
361 WaUsWest1 => String::from("s3.us-west-1.wasabisys.com"),
362 WaCaCentral1 => String::from("s3.ca-central-1.wasabisys.com"),
363 WaEuCentral1 => String::from("s3.eu-central-1.wasabisys.com"),
364 WaEuCentral2 => String::from("s3.eu-central-2.wasabisys.com"),
365 WaEuWest1 => String::from("s3.eu-west-1.wasabisys.com"),
366 WaEuWest2 => String::from("s3.eu-west-2.wasabisys.com"),
367 WaApNortheast1 => String::from("s3.ap-northeast-1.wasabisys.com"),
368 WaApNortheast2 => String::from("s3.ap-northeast-2.wasabisys.com"),
369 WaApSoutheast1 => String::from("s3.ap-southeast-1.wasabisys.com"),
370 WaApSoutheast2 => String::from("s3.ap-southeast-2.wasabisys.com"),
371 OvhGra => String::from("s3.gra.io.cloud.ovh.net"),
372 OvhRbx => String::from("s3.rbx.io.cloud.ovh.net"),
373 OvhSbg => String::from("s3.sbg.io.cloud.ovh.net"),
374 OvhDe => String::from("s3.de.io.cloud.ovh.net"),
375 OvhUk => String::from("s3.uk.io.cloud.ovh.net"),
376 OvhWaw => String::from("s3.waw.io.cloud.ovh.net"),
377 OvhBhs => String::from("s3.bhs.io.cloud.ovh.net"),
378 OvhCaEastTor => String::from("s3.ca-east-tor.io.cloud.ovh.net"),
379 OvhSgp => String::from("s3.sgp.io.cloud.ovh.net"),
380 R2 { ref account_id } => format!("{}.r2.cloudflarestorage.com", account_id),
381 R2Eu { ref account_id } => format!("{}.eu.r2.cloudflarestorage.com", account_id),
382 Custom { ref endpoint, .. } => endpoint.to_string(),
383 }
384 }
385
386 pub fn scheme(&self) -> String {
387 match *self {
388 Region::Custom { ref endpoint, .. } => match endpoint.find("://") {
389 Some(pos) => endpoint[..pos].to_string(),
390 None => "https".to_string(),
391 },
392 _ => "https".to_string(),
393 }
394 }
395
396 pub fn host(&self) -> String {
397 match *self {
398 Region::Custom { ref endpoint, .. } => {
399 let host = match endpoint.find("://") {
400 Some(pos) => endpoint[pos + 3..].to_string(),
401 None => endpoint.to_string(),
402 };
403 host.trim_end_matches('/').to_string()
406 }
407 _ => self.endpoint(),
408 }
409 }
410
411 pub fn from_env(region_env: &str, endpoint_env: Option<&str>) -> Result<Region, RegionError> {
412 if let Some(endpoint_env) = endpoint_env {
413 Ok(Region::Custom {
414 region: env::var(region_env)?,
415 endpoint: env::var(endpoint_env)?,
416 })
417 } else {
418 Ok(Region::from_str(&env::var(region_env)?)?)
419 }
420 }
421
422 pub fn from_default_env() -> Result<Region, RegionError> {
424 if let Ok(endpoint) = env::var("AWS_ENDPOINT") {
425 Ok(Region::Custom {
426 region: env::var("AWS_REGION")?,
427 endpoint,
428 })
429 } else {
430 Ok(Region::from_str(&env::var("AWS_REGION")?)?)
431 }
432 }
433}
434
435#[test]
436fn yandex_object_storage() {
437 let yandex = Region::Custom {
438 endpoint: "storage.yandexcloud.net".to_string(),
439 region: "ru-central1".to_string(),
440 };
441
442 let yandex_region = "ru-central1".parse::<Region>().unwrap();
443
444 assert_eq!(yandex.endpoint(), yandex_region.endpoint());
445
446 assert_eq!(yandex.to_string(), yandex_region.to_string());
447}
448
449#[test]
450fn test_region_eu_central_2() {
451 let region = "eu-central-2".parse::<Region>().unwrap();
452 assert_eq!(region.endpoint(), "s3.eu-central-2.amazonaws.com");
453}
454
455#[test]
456fn test_region_me_central_1() {
457 let region = "me-central-1".parse::<Region>().unwrap();
458 assert_eq!(region.endpoint(), "s3.me-central-1.amazonaws.com");
459 assert_eq!(region.to_string(), "me-central-1");
460}
461
462#[test]
463fn test_region_ap_east_2() {
464 let region = "ap-east-2".parse::<Region>().unwrap();
465 assert_eq!(region.endpoint(), "s3.ap-east-2.amazonaws.com");
466 assert_eq!(region.to_string(), "ap-east-2");
467}
468
469#[test]
470fn test_region_ap_south_2() {
471 let region = "ap-south-2".parse::<Region>().unwrap();
472 assert_eq!(region.endpoint(), "s3.ap-south-2.amazonaws.com");
473 assert_eq!(region.to_string(), "ap-south-2");
474}
475
476#[test]
477fn test_region_ap_southeast_3() {
478 let region = "ap-southeast-3".parse::<Region>().unwrap();
479 assert_eq!(region.endpoint(), "s3.ap-southeast-3.amazonaws.com");
480 assert_eq!(region.to_string(), "ap-southeast-3");
481}
482
483#[test]
484fn test_region_ap_southeast_4() {
485 let region = "ap-southeast-4".parse::<Region>().unwrap();
486 assert_eq!(region.endpoint(), "s3.ap-southeast-4.amazonaws.com");
487 assert_eq!(region.to_string(), "ap-southeast-4");
488}
489
490#[test]
491fn test_region_ap_southeast_5() {
492 let region = "ap-southeast-5".parse::<Region>().unwrap();
493 assert_eq!(region.endpoint(), "s3.ap-southeast-5.amazonaws.com");
494 assert_eq!(region.to_string(), "ap-southeast-5");
495}
496
497#[test]
498fn test_region_ap_southeast_6() {
499 let region = "ap-southeast-6".parse::<Region>().unwrap();
500 assert_eq!(region.endpoint(), "s3.ap-southeast-6.amazonaws.com");
501 assert_eq!(region.to_string(), "ap-southeast-6");
502}
503
504#[test]
505fn test_region_ap_southeast_7() {
506 let region = "ap-southeast-7".parse::<Region>().unwrap();
507 assert_eq!(region.endpoint(), "s3.ap-southeast-7.amazonaws.com");
508 assert_eq!(region.to_string(), "ap-southeast-7");
509}
510
511#[test]
512fn test_region_ca_west_1() {
513 let region = "ca-west-1".parse::<Region>().unwrap();
514 assert_eq!(region.endpoint(), "s3.ca-west-1.amazonaws.com");
515 assert_eq!(region.to_string(), "ca-west-1");
516}
517
518#[test]
519fn test_region_eu_south_1() {
520 let region = "eu-south-1".parse::<Region>().unwrap();
521 assert_eq!(region.endpoint(), "s3.eu-south-1.amazonaws.com");
522 assert_eq!(region.to_string(), "eu-south-1");
523}
524
525#[test]
526fn test_region_eu_south_2() {
527 let region = "eu-south-2".parse::<Region>().unwrap();
528 assert_eq!(region.endpoint(), "s3.eu-south-2.amazonaws.com");
529 assert_eq!(region.to_string(), "eu-south-2");
530}
531
532#[test]
533fn test_region_mx_central_1() {
534 let region = "mx-central-1".parse::<Region>().unwrap();
535 assert_eq!(region.endpoint(), "s3.mx-central-1.amazonaws.com");
536 assert_eq!(region.to_string(), "mx-central-1");
537}
538
539#[test]
540fn test_custom_endpoint_trailing_slash() {
541 let region_with_slash = Region::Custom {
543 region: "eu-central-1".to_owned(),
544 endpoint: "https://s3.gra.io.cloud.ovh.net/".to_owned(),
545 };
546 assert_eq!(region_with_slash.host(), "s3.gra.io.cloud.ovh.net");
547
548 let region_without_slash = Region::Custom {
550 region: "eu-central-1".to_owned(),
551 endpoint: "https://s3.gra.io.cloud.ovh.net".to_owned(),
552 };
553 assert_eq!(region_without_slash.host(), "s3.gra.io.cloud.ovh.net");
554
555 let region_multiple_slashes = Region::Custom {
557 region: "eu-central-1".to_owned(),
558 endpoint: "https://s3.example.com///".to_owned(),
559 };
560 assert_eq!(region_multiple_slashes.host(), "s3.example.com");
561
562 let region_with_port = Region::Custom {
564 region: "eu-central-1".to_owned(),
565 endpoint: "http://localhost:9000/".to_owned(),
566 };
567 assert_eq!(region_with_port.host(), "localhost:9000");
568}