object_store/aws/
resolve.rs1use crate::aws::STORE;
19use crate::{ClientOptions, Result};
20
21#[derive(Debug, thiserror::Error)]
23enum Error {
24 #[error("Bucket '{}' not found", bucket)]
25 BucketNotFound { bucket: String },
26
27 #[error("Failed to resolve region for bucket '{}'", bucket)]
28 ResolveRegion {
29 bucket: String,
30 source: reqwest::Error,
31 },
32
33 #[error("Failed to parse the region for bucket '{}'", bucket)]
34 RegionParse { bucket: String },
35}
36
37impl From<Error> for crate::Error {
38 fn from(source: Error) -> Self {
39 Self::Generic {
40 store: STORE,
41 source: Box::new(source),
42 }
43 }
44}
45
46pub async fn resolve_bucket_region(bucket: &str, client_options: &ClientOptions) -> Result<String> {
50 use reqwest::StatusCode;
51
52 let endpoint = format!("https://{bucket}.s3.amazonaws.com");
53
54 let client = client_options.client()?;
55
56 let response = client.head(&endpoint).send().await.map_err(|source| {
57 let bucket = bucket.into();
58 Error::ResolveRegion { bucket, source }
59 })?;
60
61 if response.status() == StatusCode::NOT_FOUND {
62 let bucket = bucket.into();
63 return Err(Error::BucketNotFound { bucket }.into());
64 }
65
66 let region = response
67 .headers()
68 .get("x-amz-bucket-region")
69 .and_then(|x| x.to_str().ok())
70 .ok_or_else(|| Error::RegionParse {
71 bucket: bucket.into(),
72 })?;
73
74 Ok(region.to_string())
75}
76
77#[cfg(test)]
78mod tests {
79 use super::*;
80
81 #[tokio::test]
82 async fn test_bucket_does_not_exist() {
83 let bucket = "please-dont-exist";
84
85 let result = resolve_bucket_region(bucket, &ClientOptions::new()).await;
86
87 assert!(result.is_err());
88 }
89}