Skip to content

Commit f7263e2

Browse files
authored
object_store: Migrate from snafu to thiserror (#6266)
* object_store: Add `thiserror` dependency * object_store/memory: Migrate from `snafu` to `thiserror` * object_store/parse: Migrate from `snafu` to `thiserror` * object_store/util: Migrate from `snafu` to `thiserror` * object_store/local: Migrate from `snafu` to `thiserror` * object_store/delimited: Migrate from `snafu` to `thiserror` * object_store/path/parts: Migrate from `snafu` to `thiserror` * object_store/path: Migrate from `snafu` to `thiserror` * object_store/http: Migrate from `snafu` to `thiserror` * object_store/client: Migrate from `snafu` to `thiserror` * object_store/aws: Migrate from `snafu` to `thiserror` * object_store/azure: Migrate from `snafu` to `thiserror` * object_store/gcp: Migrate from `snafu` to `thiserror` * object_store/lib: Migrate from `snafu` to `thiserror` * Remove `snafu` dependency
1 parent 4a0bdde commit f7263e2

24 files changed

+620
-528
lines changed

object_store/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ humantime = "2.1"
3838
itertools = "0.13.0"
3939
parking_lot = { version = "0.12" }
4040
percent-encoding = "2.1"
41-
snafu = { version = "0.8", default-features = false, features = ["std", "rust_1_61"] }
41+
thiserror = "2.0.2"
4242
tracing = { version = "0.1" }
4343
url = "2.2"
4444
walkdir = { version = "2", optional = true }

object_store/src/aws/builder.rs

Lines changed: 32 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,6 @@ use itertools::Itertools;
3232
use md5::{Digest, Md5};
3333
use reqwest::header::{HeaderMap, HeaderValue};
3434
use serde::{Deserialize, Serialize};
35-
use snafu::{OptionExt, ResultExt, Snafu};
3635
use std::str::FromStr;
3736
use std::sync::Arc;
3837
use std::time::Duration;
@@ -43,46 +42,46 @@ use url::Url;
4342
static DEFAULT_METADATA_ENDPOINT: &str = "http://169.254.169.254";
4443

4544
/// A specialized `Error` for object store-related errors
46-
#[derive(Debug, Snafu)]
45+
#[derive(Debug, thiserror::Error)]
4746
enum Error {
48-
#[snafu(display("Missing bucket name"))]
47+
#[error("Missing bucket name")]
4948
MissingBucketName,
5049

51-
#[snafu(display("Missing AccessKeyId"))]
50+
#[error("Missing AccessKeyId")]
5251
MissingAccessKeyId,
5352

54-
#[snafu(display("Missing SecretAccessKey"))]
53+
#[error("Missing SecretAccessKey")]
5554
MissingSecretAccessKey,
5655

57-
#[snafu(display("Unable parse source url. Url: {}, Error: {}", url, source))]
56+
#[error("Unable parse source url. Url: {}, Error: {}", url, source)]
5857
UnableToParseUrl {
5958
source: url::ParseError,
6059
url: String,
6160
},
6261

63-
#[snafu(display(
62+
#[error(
6463
"Unknown url scheme cannot be parsed into storage location: {}",
6564
scheme
66-
))]
65+
)]
6766
UnknownUrlScheme { scheme: String },
6867

69-
#[snafu(display("URL did not match any known pattern for scheme: {}", url))]
68+
#[error("URL did not match any known pattern for scheme: {}", url)]
7069
UrlNotRecognised { url: String },
7170

72-
#[snafu(display("Configuration key: '{}' is not known.", key))]
71+
#[error("Configuration key: '{}' is not known.", key)]
7372
UnknownConfigurationKey { key: String },
7473

75-
#[snafu(display("Invalid Zone suffix for bucket '{bucket}'"))]
74+
#[error("Invalid Zone suffix for bucket '{bucket}'")]
7675
ZoneSuffix { bucket: String },
7776

78-
#[snafu(display("Invalid encryption type: {}. Valid values are \"AES256\", \"sse:kms\", \"sse:kms:dsse\" and \"sse-c\".", passed))]
77+
#[error("Invalid encryption type: {}. Valid values are \"AES256\", \"sse:kms\", \"sse:kms:dsse\" and \"sse-c\".", passed)]
7978
InvalidEncryptionType { passed: String },
8079

81-
#[snafu(display(
80+
#[error(
8281
"Invalid encryption header values. Header: {}, source: {}",
8382
header,
8483
source
85-
))]
84+
)]
8685
InvalidEncryptionHeader {
8786
header: &'static str,
8887
source: Box<dyn std::error::Error + Send + Sync + 'static>,
@@ -603,8 +602,15 @@ impl AmazonS3Builder {
603602
/// This is a separate member function to allow fallible computation to
604603
/// be deferred until [`Self::build`] which in turn allows deriving [`Clone`]
605604
fn parse_url(&mut self, url: &str) -> Result<()> {
606-
let parsed = Url::parse(url).context(UnableToParseUrlSnafu { url })?;
607-
let host = parsed.host_str().context(UrlNotRecognisedSnafu { url })?;
605+
let parsed = Url::parse(url).map_err(|source| {
606+
let url = url.into();
607+
Error::UnableToParseUrl { url, source }
608+
})?;
609+
610+
let host = parsed
611+
.host_str()
612+
.ok_or_else(|| Error::UrlNotRecognised { url: url.into() })?;
613+
608614
match parsed.scheme() {
609615
"s3" | "s3a" => self.bucket_name = Some(host.to_string()),
610616
"https" => match host.splitn(4, '.').collect_tuple() {
@@ -630,9 +636,12 @@ impl AmazonS3Builder {
630636
self.bucket_name = Some(bucket.into());
631637
}
632638
}
633-
_ => return Err(UrlNotRecognisedSnafu { url }.build().into()),
639+
_ => return Err(Error::UrlNotRecognised { url: url.into() }.into()),
634640
},
635-
scheme => return Err(UnknownUrlSchemeSnafu { scheme }.build().into()),
641+
scheme => {
642+
let scheme = scheme.into();
643+
return Err(Error::UnknownUrlScheme { scheme }.into());
644+
}
636645
};
637646
Ok(())
638647
}
@@ -875,7 +884,7 @@ impl AmazonS3Builder {
875884
self.parse_url(&url)?;
876885
}
877886

878-
let bucket = self.bucket_name.context(MissingBucketNameSnafu)?;
887+
let bucket = self.bucket_name.ok_or(Error::MissingBucketName)?;
879888
let region = self.region.unwrap_or_else(|| "us-east-1".to_string());
880889
let checksum = self.checksum_algorithm.map(|x| x.get()).transpose()?;
881890
let copy_if_not_exists = self.copy_if_not_exists.map(|x| x.get()).transpose()?;
@@ -957,7 +966,10 @@ impl AmazonS3Builder {
957966

958967
let (session_provider, zonal_endpoint) = match self.s3_express.get()? {
959968
true => {
960-
let zone = parse_bucket_az(&bucket).context(ZoneSuffixSnafu { bucket: &bucket })?;
969+
let zone = parse_bucket_az(&bucket).ok_or_else(|| {
970+
let bucket = bucket.clone();
971+
Error::ZoneSuffix { bucket }
972+
})?;
961973

962974
// https://docs.aws.amazon.com/AmazonS3/latest/userguide/s3-express-Regions-and-Zones.html
963975
let endpoint = format!("https://{bucket}.s3express-{zone}.{region}.amazonaws.com");

object_store/src/aws/client.rs

Lines changed: 49 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,6 @@ use reqwest::{Client as ReqwestClient, Method, RequestBuilder, Response};
5656
use ring::digest;
5757
use ring::digest::Context;
5858
use serde::{Deserialize, Serialize};
59-
use snafu::{ResultExt, Snafu};
6059
use std::sync::Arc;
6160

6261
const VERSION_HEADER: &str = "x-amz-version-id";
@@ -65,56 +64,56 @@ const USER_DEFINED_METADATA_HEADER_PREFIX: &str = "x-amz-meta-";
6564
const ALGORITHM: &str = "x-amz-checksum-algorithm";
6665

6766
/// A specialized `Error` for object store-related errors
68-
#[derive(Debug, Snafu)]
67+
#[derive(Debug, thiserror::Error)]
6968
pub(crate) enum Error {
70-
#[snafu(display("Error performing DeleteObjects request: {}", source))]
69+
#[error("Error performing DeleteObjects request: {}", source)]
7170
DeleteObjectsRequest { source: crate::client::retry::Error },
7271

73-
#[snafu(display(
72+
#[error(
7473
"DeleteObjects request failed for key {}: {} (code: {})",
7574
path,
7675
message,
7776
code
78-
))]
77+
)]
7978
DeleteFailed {
8079
path: String,
8180
code: String,
8281
message: String,
8382
},
8483

85-
#[snafu(display("Error getting DeleteObjects response body: {}", source))]
84+
#[error("Error getting DeleteObjects response body: {}", source)]
8685
DeleteObjectsResponse { source: reqwest::Error },
8786

88-
#[snafu(display("Got invalid DeleteObjects response: {}", source))]
87+
#[error("Got invalid DeleteObjects response: {}", source)]
8988
InvalidDeleteObjectsResponse {
9089
source: Box<dyn std::error::Error + Send + Sync + 'static>,
9190
},
9291

93-
#[snafu(display("Error performing list request: {}", source))]
92+
#[error("Error performing list request: {}", source)]
9493
ListRequest { source: crate::client::retry::Error },
9594

96-
#[snafu(display("Error getting list response body: {}", source))]
95+
#[error("Error getting list response body: {}", source)]
9796
ListResponseBody { source: reqwest::Error },
9897

99-
#[snafu(display("Error getting create multipart response body: {}", source))]
98+
#[error("Error getting create multipart response body: {}", source)]
10099
CreateMultipartResponseBody { source: reqwest::Error },
101100

102-
#[snafu(display("Error performing complete multipart request: {}: {}", path, source))]
101+
#[error("Error performing complete multipart request: {}: {}", path, source)]
103102
CompleteMultipartRequest {
104103
source: crate::client::retry::Error,
105104
path: String,
106105
},
107106

108-
#[snafu(display("Error getting complete multipart response body: {}", source))]
107+
#[error("Error getting complete multipart response body: {}", source)]
109108
CompleteMultipartResponseBody { source: reqwest::Error },
110109

111-
#[snafu(display("Got invalid list response: {}", source))]
110+
#[error("Got invalid list response: {}", source)]
112111
InvalidListResponse { source: quick_xml::de::DeError },
113112

114-
#[snafu(display("Got invalid multipart response: {}", source))]
113+
#[error("Got invalid multipart response: {}", source)]
115114
InvalidMultipartResponse { source: quick_xml::de::DeError },
116115

117-
#[snafu(display("Unable to extract metadata from headers: {}", source))]
116+
#[error("Unable to extract metadata from headers: {}", source)]
118117
Metadata {
119118
source: crate::client::header::Error,
120119
},
@@ -263,10 +262,15 @@ impl SessionCredential<'_> {
263262
}
264263
}
265264

266-
#[derive(Debug, Snafu)]
265+
#[derive(Debug, thiserror::Error)]
267266
pub enum RequestError {
268-
#[snafu(context(false))]
269-
Generic { source: crate::Error },
267+
#[error(transparent)]
268+
Generic {
269+
#[from]
270+
source: crate::Error,
271+
},
272+
273+
#[error("Retry")]
270274
Retry {
271275
source: crate::client::retry::Error,
272276
path: String,
@@ -426,12 +430,16 @@ impl<'a> Request<'a> {
426430
.payload(self.payload)
427431
.send()
428432
.await
429-
.context(RetrySnafu { path })
433+
.map_err(|source| {
434+
let path = path.into();
435+
RequestError::Retry { source, path }
436+
})
430437
}
431438

432439
pub(crate) async fn do_put(self) -> Result<PutResult> {
433440
let response = self.send().await?;
434-
Ok(get_put_result(response.headers(), VERSION_HEADER).context(MetadataSnafu)?)
441+
Ok(get_put_result(response.headers(), VERSION_HEADER)
442+
.map_err(|source| Error::Metadata { source })?)
435443
}
436444
}
437445

@@ -535,10 +543,10 @@ impl S3Client {
535543
.with_aws_sigv4(credential.authorizer(), Some(digest.as_ref()))
536544
.send_retry(&self.config.retry_config)
537545
.await
538-
.context(DeleteObjectsRequestSnafu {})?
546+
.map_err(|source| Error::DeleteObjectsRequest { source })?
539547
.bytes()
540548
.await
541-
.context(DeleteObjectsResponseSnafu {})?;
549+
.map_err(|source| Error::DeleteObjectsResponse { source })?;
542550

543551
let response: BatchDeleteResponse =
544552
quick_xml::de::from_reader(response.reader()).map_err(|err| {
@@ -635,10 +643,10 @@ impl S3Client {
635643
.await?
636644
.bytes()
637645
.await
638-
.context(CreateMultipartResponseBodySnafu)?;
646+
.map_err(|source| Error::CreateMultipartResponseBody { source })?;
639647

640-
let response: InitiateMultipartUploadResult =
641-
quick_xml::de::from_reader(response.reader()).context(InvalidMultipartResponseSnafu)?;
648+
let response: InitiateMultipartUploadResult = quick_xml::de::from_reader(response.reader())
649+
.map_err(|source| Error::InvalidMultipartResponse { source })?;
642650

643651
Ok(response.upload_id)
644652
}
@@ -683,14 +691,14 @@ impl S3Client {
683691
.map(|v| v.to_string());
684692

685693
let e_tag = match is_copy {
686-
false => get_etag(response.headers()).context(MetadataSnafu)?,
694+
false => get_etag(response.headers()).map_err(|source| Error::Metadata { source })?,
687695
true => {
688696
let response = response
689697
.bytes()
690698
.await
691-
.context(CreateMultipartResponseBodySnafu)?;
699+
.map_err(|source| Error::CreateMultipartResponseBody { source })?;
692700
let response: CopyPartResult = quick_xml::de::from_reader(response.reader())
693-
.context(InvalidMultipartResponseSnafu)?;
701+
.map_err(|source| Error::InvalidMultipartResponse { source })?;
694702
response.e_tag
695703
}
696704
};
@@ -764,19 +772,21 @@ impl S3Client {
764772
.retry_error_body(true)
765773
.send()
766774
.await
767-
.context(CompleteMultipartRequestSnafu {
768-
path: location.as_ref(),
775+
.map_err(|source| Error::CompleteMultipartRequest {
776+
source,
777+
path: location.as_ref().to_string(),
769778
})?;
770779

771-
let version = get_version(response.headers(), VERSION_HEADER).context(MetadataSnafu)?;
780+
let version = get_version(response.headers(), VERSION_HEADER)
781+
.map_err(|source| Error::Metadata { source })?;
772782

773783
let data = response
774784
.bytes()
775785
.await
776-
.context(CompleteMultipartResponseBodySnafu)?;
786+
.map_err(|source| Error::CompleteMultipartResponseBody { source })?;
777787

778-
let response: CompleteMultipartUploadResult =
779-
quick_xml::de::from_reader(data.reader()).context(InvalidMultipartResponseSnafu)?;
788+
let response: CompleteMultipartUploadResult = quick_xml::de::from_reader(data.reader())
789+
.map_err(|source| Error::InvalidMultipartResponse { source })?;
780790

781791
Ok(PutResult {
782792
e_tag: Some(response.e_tag),
@@ -884,13 +894,14 @@ impl ListClient for S3Client {
884894
.with_aws_sigv4(credential.authorizer(), None)
885895
.send_retry(&self.config.retry_config)
886896
.await
887-
.context(ListRequestSnafu)?
897+
.map_err(|source| Error::ListRequest { source })?
888898
.bytes()
889899
.await
890-
.context(ListResponseBodySnafu)?;
900+
.map_err(|source| Error::ListResponseBody { source })?;
901+
902+
let mut response: ListResponse = quick_xml::de::from_reader(response.reader())
903+
.map_err(|source| Error::InvalidListResponse { source })?;
891904

892-
let mut response: ListResponse =
893-
quick_xml::de::from_reader(response.reader()).context(InvalidListResponseSnafu)?;
894905
let token = response.next_continuation_token.take();
895906

896907
Ok((response.try_into()?, token))

object_store/src/aws/credential.rs

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -29,23 +29,22 @@ use percent_encoding::utf8_percent_encode;
2929
use reqwest::header::{HeaderMap, HeaderValue, AUTHORIZATION};
3030
use reqwest::{Client, Method, Request, RequestBuilder, StatusCode};
3131
use serde::Deserialize;
32-
use snafu::{ResultExt, Snafu};
3332
use std::collections::BTreeMap;
3433
use std::sync::Arc;
3534
use std::time::{Duration, Instant};
3635
use tracing::warn;
3736
use url::Url;
3837

39-
#[derive(Debug, Snafu)]
38+
#[derive(Debug, thiserror::Error)]
4039
#[allow(clippy::enum_variant_names)]
4140
enum Error {
42-
#[snafu(display("Error performing CreateSession request: {source}"))]
41+
#[error("Error performing CreateSession request: {source}")]
4342
CreateSessionRequest { source: crate::client::retry::Error },
4443

45-
#[snafu(display("Error getting CreateSession response: {source}"))]
44+
#[error("Error getting CreateSession response: {source}")]
4645
CreateSessionResponse { source: reqwest::Error },
4746

48-
#[snafu(display("Invalid CreateSessionOutput response: {source}"))]
47+
#[error("Invalid CreateSessionOutput response: {source}")]
4948
CreateSessionOutput { source: quick_xml::DeError },
5049
}
5150

@@ -726,13 +725,13 @@ impl TokenProvider for SessionProvider {
726725
.with_aws_sigv4(Some(authorizer), None)
727726
.send_retry(retry)
728727
.await
729-
.context(CreateSessionRequestSnafu)?
728+
.map_err(|source| Error::CreateSessionRequest { source })?
730729
.bytes()
731730
.await
732-
.context(CreateSessionResponseSnafu)?;
731+
.map_err(|source| Error::CreateSessionResponse { source })?;
733732

734-
let resp: CreateSessionOutput =
735-
quick_xml::de::from_reader(bytes.reader()).context(CreateSessionOutputSnafu)?;
733+
let resp: CreateSessionOutput = quick_xml::de::from_reader(bytes.reader())
734+
.map_err(|source| Error::CreateSessionOutput { source })?;
736735

737736
let creds = resp.credentials;
738737
Ok(TemporaryToken {

0 commit comments

Comments
 (0)