opendal 0.1.9 → 0.1.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/Cargo.lock +89 -56
- data/Cargo.toml +1 -1
- data/DEPENDENCIES.rust.tsv +55 -41
- data/LICENSE +201 -0
- data/LICENSE-MPL-2.0.txt +373 -0
- data/NOTICE +5 -0
- data/core/CHANGELOG.md +175 -0
- data/core/Cargo.lock +225 -298
- data/core/Cargo.toml +100 -94
- data/core/DEPENDENCIES.rust.tsv +44 -73
- data/core/benches/metadata/README.md +21 -0
- data/core/benches/metadata/main.rs +541 -0
- data/core/benches/vs_fs/Cargo.toml +1 -1
- data/core/benches/vs_s3/Cargo.toml +1 -1
- data/core/core/Cargo.toml +7 -2
- data/core/core/src/blocking/delete.rs +5 -1
- data/core/core/src/blocking/operator.rs +12 -0
- data/core/core/src/blocking/read/std_reader.rs +25 -0
- data/core/core/src/docs/concepts.rs +70 -94
- data/core/core/src/docs/internals/accessor.rs +62 -293
- data/core/core/src/docs/internals/layer.rs +43 -59
- data/core/core/src/docs/internals/mod.rs +21 -23
- data/core/core/src/docs/mod.rs +10 -6
- data/core/core/src/docs/performance/concurrent_write.md +92 -55
- data/core/core/src/docs/performance/http_optimization.md +120 -104
- data/core/core/src/docs/performance/mod.rs +8 -6
- data/core/core/src/docs/rfcs/5485_conditional_reader.md +4 -0
- data/core/core/src/docs/rfcs/7182_restore_api.md +139 -557
- data/core/core/src/docs/rfcs/7945_limited_read.md +210 -0
- data/core/core/src/docs/rfcs/7948_s3_provider_presets.md +254 -0
- data/core/core/src/docs/rfcs/8109_writer_copy_from.md +330 -0
- data/core/core/src/docs/rfcs/8145_conflict_error_kind.md +210 -0
- data/core/core/src/docs/rfcs/8147_write_if_not_changed.md +233 -0
- data/core/core/src/docs/rfcs/8188_compose_api.md +292 -0
- data/core/core/src/docs/rfcs/8194_compact_metadata_and_operation_args.md +444 -0
- data/core/core/src/docs/rfcs/README.md +14 -3
- data/core/core/src/docs/rfcs/mod.rs +46 -22
- data/core/core/src/docs/specs/README.md +34 -0
- data/core/core/src/docs/specs/conditional-operations.md +156 -0
- data/core/core/src/docs/specs/metadata.md +63 -0
- data/core/core/src/docs/specs/mod.rs +26 -0
- data/core/core/src/docs/upgrade.md +114 -1
- data/core/core/src/layers/capability_override.rs +15 -2
- data/core/core/src/layers/complete.rs +347 -43
- data/core/core/src/layers/correctness_check.rs +718 -51
- data/core/core/src/layers/error_context.rs +76 -2
- data/core/core/src/layers/simulate.rs +91 -28
- data/core/core/src/lib.rs +1 -0
- data/core/core/src/raw/accessor.rs +91 -15
- data/core/core/src/raw/enum_utils.rs +7 -0
- data/core/core/src/raw/http_util/header.rs +22 -17
- data/core/core/src/raw/oio/compose/api.rs +92 -0
- data/core/core/src/raw/oio/compose/mod.rs +19 -0
- data/core/core/src/raw/oio/copy/api.rs +3 -3
- data/core/core/src/raw/oio/copy/block_copy.rs +32 -4
- data/core/core/src/raw/oio/copy/multipart_copy.rs +36 -7
- data/core/core/src/raw/oio/entry.rs +48 -22
- data/core/core/src/raw/oio/list/flat_list.rs +1 -1
- data/core/core/src/raw/oio/list/hierarchy_list.rs +3 -2
- data/core/core/src/raw/oio/mod.rs +3 -0
- data/core/core/src/raw/oio/read/position_read.rs +1 -1
- data/core/core/src/raw/oio/write/api.rs +40 -0
- data/core/core/src/raw/oio/write/append_write.rs +4 -4
- data/core/core/src/raw/oio/write/block_write.rs +2 -2
- data/core/core/src/raw/oio/write/multipart_write.rs +181 -48
- data/core/core/src/raw/oio/write/position_write.rs +1 -1
- data/core/core/src/raw/operation.rs +6 -0
- data/core/core/src/raw/ops.rs +1191 -456
- data/core/core/src/raw/rps.rs +11 -0
- data/core/core/src/raw/time.rs +14 -3
- data/core/core/src/services/memory/backend.rs +5 -10
- data/core/core/src/services/memory/lister.rs +4 -4
- data/core/core/src/services/memory/writer.rs +7 -6
- data/core/core/src/types/buffer.rs +102 -12
- data/core/core/src/types/bytes_range.rs +20 -1
- data/core/core/src/types/capability.rs +65 -0
- data/core/core/src/types/compact.rs +329 -0
- data/core/core/src/types/compose/composer.rs +183 -0
- data/core/core/src/types/compose/input.rs +57 -0
- data/core/core/src/types/compose/mod.rs +23 -0
- data/core/core/src/types/context/read.rs +16 -11
- data/core/core/src/types/context/write.rs +150 -2
- data/core/core/src/types/copy.rs +5 -3
- data/core/core/src/types/delete/deleter.rs +20 -12
- data/core/core/src/types/delete/input.rs +22 -64
- data/core/core/src/types/delete/mod.rs +0 -1
- data/core/core/src/types/entry.rs +8 -14
- data/core/core/src/types/error.rs +28 -11
- data/core/core/src/types/metadata.rs +709 -322
- data/core/core/src/types/mod.rs +9 -1
- data/core/core/src/types/operator/operator.rs +278 -29
- data/core/core/src/types/operator/operator_futures.rs +466 -105
- data/core/core/src/types/options.rs +631 -158
- data/core/core/src/types/read/futures_async_reader.rs +663 -50
- data/core/core/src/types/read/reader.rs +23 -5
- data/core/core/src/types/write/futures_async_writer.rs +27 -0
- data/core/core/src/types/write/writer.rs +47 -5
- data/core/http-transports/reqwest/Cargo.toml +1 -1
- data/core/layers/async-backtrace/Cargo.toml +2 -2
- data/core/layers/async-backtrace/src/lib.rs +21 -2
- data/core/layers/await-tree/Cargo.toml +2 -2
- data/core/layers/await-tree/src/lib.rs +25 -2
- data/core/layers/capability-check/Cargo.toml +2 -2
- data/core/layers/capability-check/src/lib.rs +98 -10
- data/core/layers/chaos/Cargo.toml +2 -2
- data/core/layers/chaos/src/lib.rs +18 -3
- data/core/layers/concurrent-limit/Cargo.toml +3 -3
- data/core/layers/concurrent-limit/src/lib.rs +53 -13
- data/core/layers/dtrace/Cargo.toml +2 -2
- data/core/layers/dtrace/src/lib.rs +29 -2
- data/core/layers/fastmetrics/Cargo.toml +3 -3
- data/core/layers/fastrace/Cargo.toml +2 -2
- data/core/layers/fastrace/src/lib.rs +28 -2
- data/core/layers/foyer/Cargo.toml +2 -2
- data/core/layers/foyer/src/full.rs +16 -19
- data/core/layers/foyer/src/lib.rs +67 -14
- data/core/layers/hotpath/Cargo.toml +2 -2
- data/core/layers/hotpath/src/lib.rs +20 -2
- data/core/layers/immutable-index/Cargo.toml +2 -2
- data/core/layers/immutable-index/src/lib.rs +48 -24
- data/core/layers/logging/Cargo.toml +2 -2
- data/core/layers/logging/src/lib.rs +46 -2
- data/core/layers/metrics/Cargo.toml +3 -3
- data/core/layers/mime-guess/Cargo.toml +2 -2
- data/core/layers/mime-guess/src/lib.rs +31 -4
- data/core/layers/observe-metrics-common/Cargo.toml +2 -2
- data/core/layers/observe-metrics-common/src/lib.rs +55 -2
- data/core/layers/otelmetrics/Cargo.toml +3 -3
- data/core/layers/oteltrace/Cargo.toml +2 -2
- data/core/layers/oteltrace/src/lib.rs +27 -2
- data/core/layers/prometheus/Cargo.toml +3 -3
- data/core/layers/prometheus-client/Cargo.toml +3 -3
- data/core/layers/retry/Cargo.toml +4 -4
- data/core/layers/retry/src/lib.rs +76 -19
- data/core/layers/route/Cargo.toml +3 -3
- data/core/layers/route/src/lib.rs +34 -7
- data/core/layers/tail-cut/Cargo.toml +2 -2
- data/core/layers/tail-cut/src/lib.rs +28 -2
- data/core/layers/throttle/Cargo.toml +2 -2
- data/core/layers/throttle/src/lib.rs +19 -2
- data/core/layers/timeout/Cargo.toml +3 -3
- data/core/layers/timeout/src/lib.rs +38 -13
- data/core/layers/tracing/Cargo.toml +2 -2
- data/core/layers/tracing/src/lib.rs +24 -3
- data/core/services/aliyun-drive/Cargo.toml +2 -2
- data/core/services/aliyun-drive/src/backend.rs +34 -24
- data/core/services/aliyun-drive/src/core.rs +143 -54
- data/core/services/aliyun-drive/src/lister.rs +19 -18
- data/core/services/aliyun-drive/src/reader.rs +4 -1
- data/core/services/aliyun-drive/src/writer.rs +3 -3
- data/core/services/alluxio/Cargo.toml +1 -1
- data/core/services/alluxio/src/backend.rs +1 -1
- data/core/services/alluxio/src/core.rs +113 -94
- data/core/services/alluxio/src/reader.rs +7 -3
- data/core/services/alluxio/src/writer.rs +2 -2
- data/core/services/azblob/Cargo.toml +2 -2
- data/core/services/azblob/src/backend.rs +17 -9
- data/core/services/azblob/src/copier.rs +41 -15
- data/core/services/azblob/src/core.rs +372 -135
- data/core/services/azblob/src/deleter.rs +41 -18
- data/core/services/azblob/src/lister.rs +16 -10
- data/core/services/azblob/src/reader.rs +4 -1
- data/core/services/azblob/src/writer.rs +48 -15
- data/core/services/azdls/Cargo.toml +3 -3
- data/core/services/azdls/src/backend.rs +45 -15
- data/core/services/azdls/src/config.rs +4 -1
- data/core/services/azdls/src/core.rs +276 -87
- data/core/services/azdls/src/deleter.rs +13 -4
- data/core/services/azdls/src/lister.rs +18 -12
- data/core/services/azdls/src/reader.rs +6 -2
- data/core/services/azdls/src/writer.rs +44 -23
- data/core/services/azfile/Cargo.toml +2 -2
- data/core/services/azfile/src/backend.rs +24 -8
- data/core/services/azfile/src/core.rs +195 -77
- data/core/services/azfile/src/deleter.rs +8 -1
- data/core/services/azfile/src/lister.rs +20 -14
- data/core/services/azfile/src/reader.rs +5 -2
- data/core/services/azfile/src/writer.rs +27 -15
- data/core/services/azure-common/Cargo.toml +1 -1
- data/core/services/b2/Cargo.toml +2 -2
- data/core/services/b2/src/backend.rs +30 -11
- data/core/services/b2/src/core.rs +134 -120
- data/core/services/b2/src/deleter.rs +1 -1
- data/core/services/b2/src/lister.rs +5 -2
- data/core/services/b2/src/reader.rs +5 -2
- data/core/services/b2/src/writer.rs +29 -12
- data/core/services/cacache/Cargo.toml +1 -1
- data/core/services/cacache/src/backend.rs +4 -5
- data/core/services/cacache/src/reader.rs +4 -1
- data/core/services/cacache/src/writer.rs +6 -2
- data/core/services/cloudflare-kv/Cargo.toml +1 -1
- data/core/services/cloudflare-kv/src/backend.rs +26 -15
- data/core/services/cloudflare-kv/src/config.rs +13 -2
- data/core/services/cloudflare-kv/src/core.rs +62 -56
- data/core/services/cloudflare-kv/src/deleter.rs +12 -3
- data/core/services/cloudflare-kv/src/lister.rs +66 -17
- data/core/services/cloudflare-kv/src/reader.rs +14 -9
- data/core/services/cloudflare-kv/src/writer.rs +10 -7
- data/core/services/compfs/Cargo.toml +1 -1
- data/core/services/compfs/src/backend.rs +11 -7
- data/core/services/compfs/src/lister.rs +7 -7
- data/core/services/compfs/src/writer.rs +1 -1
- data/core/services/cos/Cargo.toml +1 -1
- data/core/services/cos/src/backend.rs +129 -16
- data/core/services/cos/src/core.rs +198 -80
- data/core/services/cos/src/deleter.rs +4 -1
- data/core/services/cos/src/lister.rs +42 -27
- data/core/services/cos/src/reader.rs +6 -2
- data/core/services/cos/src/writer.rs +45 -21
- data/core/services/d1/Cargo.toml +1 -1
- data/core/services/d1/src/backend.rs +6 -5
- data/core/services/d1/src/core.rs +79 -63
- data/core/services/d1/src/reader.rs +4 -1
- data/core/services/d1/src/writer.rs +6 -2
- data/core/services/dashmap/Cargo.toml +1 -1
- data/core/services/dashmap/src/backend.rs +2 -2
- data/core/services/dashmap/src/reader.rs +4 -1
- data/core/services/dashmap/src/writer.rs +11 -8
- data/core/services/dbfs/Cargo.toml +1 -1
- data/core/services/dbfs/src/backend.rs +21 -15
- data/core/services/dbfs/src/core.rs +65 -60
- data/core/services/dbfs/src/deleter.rs +4 -1
- data/core/services/dbfs/src/lister.rs +10 -10
- data/core/services/dbfs/src/writer.rs +6 -3
- data/core/services/dropbox/Cargo.toml +2 -2
- data/core/services/dropbox/src/backend.rs +30 -23
- data/core/services/dropbox/src/core.rs +65 -63
- data/core/services/dropbox/src/deleter.rs +1 -1
- data/core/services/dropbox/src/lister.rs +17 -8
- data/core/services/dropbox/src/reader.rs +4 -1
- data/core/services/dropbox/src/writer.rs +13 -10
- data/core/services/etcd/Cargo.toml +1 -1
- data/core/services/etcd/src/backend.rs +8 -5
- data/core/services/etcd/src/core.rs +5 -11
- data/core/services/etcd/src/lister.rs +6 -1
- data/core/services/etcd/src/reader.rs +4 -1
- data/core/services/etcd/src/writer.rs +6 -3
- data/core/services/foundationdb/Cargo.toml +1 -1
- data/core/services/foundationdb/src/backend.rs +6 -5
- data/core/services/foundationdb/src/reader.rs +4 -1
- data/core/services/foundationdb/src/writer.rs +6 -2
- data/core/services/foyer/Cargo.toml +3 -3
- data/core/services/foyer/src/backend.rs +6 -5
- data/core/services/foyer/src/reader.rs +4 -1
- data/core/services/foyer/src/writer.rs +6 -2
- data/core/services/fs/Cargo.toml +1 -1
- data/core/services/fs/src/backend.rs +4 -3
- data/core/services/fs/src/core.rs +44 -26
- data/core/services/fs/src/lister.rs +9 -6
- data/core/services/fs/src/writer.rs +20 -13
- data/core/services/ftp/Cargo.toml +4 -6
- data/core/services/ftp/src/backend.rs +9 -5
- data/core/services/ftp/src/core.rs +9 -7
- data/core/services/ftp/src/lister.rs +7 -8
- data/core/services/ftp/src/reader.rs +3 -3
- data/core/services/ftp/src/writer.rs +3 -3
- data/core/services/gcs/Cargo.toml +4 -2
- data/core/services/gcs/src/backend.rs +53 -7
- data/core/services/gcs/src/composer.rs +705 -0
- data/core/services/gcs/src/copier.rs +14 -4
- data/core/services/gcs/src/core.rs +828 -124
- data/core/services/gcs/src/deleter.rs +33 -18
- data/core/services/gcs/src/lib.rs +1 -0
- data/core/services/gcs/src/lister.rs +21 -12
- data/core/services/gcs/src/reader.rs +12 -5
- data/core/services/gcs/src/writer.rs +632 -10
- data/core/services/gcs-grpc/Cargo.toml +58 -0
- data/core/services/gcs-grpc/README.md +19 -0
- data/core/services/gcs-grpc/proto/README.md +23 -0
- data/core/services/gcs-grpc/proto/google/storage/v2/storage.proto +171 -0
- data/core/services/gcs-grpc/proto/upstream-descriptor.bin +0 -0
- data/core/services/gcs-grpc/src/backend.rs +449 -0
- data/core/services/gcs-grpc/src/config.rs +116 -0
- data/core/services/gcs-grpc/src/copier.rs +98 -0
- data/core/services/gcs-grpc/src/core.rs +298 -0
- data/core/services/gcs-grpc/src/deleter.rs +65 -0
- data/core/services/gcs-grpc/src/docs.md +19 -0
- data/core/services/gcs-grpc/src/generated/google.storage.v2.rs +541 -0
- data/core/services/gcs-grpc/src/generated/mod.rs +24 -0
- data/core/services/gcs-grpc/src/lib.rs +43 -0
- data/core/services/gcs-grpc/src/lister.rs +125 -0
- data/core/services/gcs-grpc/src/reader.rs +140 -0
- data/core/services/gcs-grpc/src/writer.rs +780 -0
- data/core/services/gdrive/Cargo.toml +2 -2
- data/core/services/gdrive/src/backend.rs +123 -69
- data/core/services/gdrive/src/core.rs +87 -69
- data/core/services/gdrive/src/deleter.rs +8 -2
- data/core/services/gdrive/src/lister.rs +38 -26
- data/core/services/gdrive/src/path_index.rs +2 -2
- data/core/services/gdrive/src/reader.rs +9 -3
- data/core/services/gdrive/src/writer.rs +13 -7
- data/core/services/ghac/Cargo.toml +2 -2
- data/core/services/ghac/src/backend.rs +5 -2
- data/core/services/ghac/src/core.rs +59 -37
- data/core/services/ghac/src/reader.rs +5 -2
- data/core/services/ghac/src/writer.rs +16 -6
- data/core/services/github/Cargo.toml +1 -1
- data/core/services/github/src/backend.rs +16 -8
- data/core/services/github/src/core.rs +88 -76
- data/core/services/github/src/lister.rs +17 -15
- data/core/services/github/src/reader.rs +5 -2
- data/core/services/github/src/writer.rs +12 -6
- data/core/services/goosefs/Cargo.toml +2 -2
- data/core/services/goosefs/src/backend.rs +2 -2
- data/core/services/goosefs/src/core.rs +291 -208
- data/core/services/goosefs/src/docs.md +3 -0
- data/core/services/goosefs/src/lister.rs +2 -3
- data/core/services/goosefs/src/reader.rs +7 -3
- data/core/services/goosefs/src/writer.rs +21 -10
- data/core/services/goosefs/tests/safe_conditional_put.rs +37 -0
- data/core/services/gridfs/Cargo.toml +2 -2
- data/core/services/gridfs/src/backend.rs +12 -8
- data/core/services/gridfs/src/core.rs +1 -1
- data/core/services/gridfs/src/reader.rs +4 -1
- data/core/services/gridfs/src/writer.rs +6 -2
- data/core/services/hdfs/Cargo.toml +1 -1
- data/core/services/hdfs/src/backend.rs +1 -1
- data/core/services/hdfs/src/core.rs +7 -4
- data/core/services/hdfs/src/lister.rs +7 -9
- data/core/services/hdfs/src/writer.rs +5 -1
- data/core/services/hdfs-native/Cargo.toml +1 -1
- data/core/services/hdfs-native/src/backend.rs +1 -1
- data/core/services/hdfs-native/src/core.rs +44 -50
- data/core/services/hdfs-native/src/lister.rs +8 -10
- data/core/services/hdfs-native/src/writer.rs +5 -1
- data/core/services/hf/Cargo.toml +4 -3
- data/core/services/hf/src/backend.rs +4 -3
- data/core/services/hf/src/core.rs +907 -153
- data/core/services/hf/src/lister.rs +16 -6
- data/core/services/hf/src/reader.rs +377 -57
- data/core/services/hf/src/writer.rs +6 -2
- data/core/services/http/Cargo.toml +3 -3
- data/core/services/http/src/backend.rs +8 -6
- data/core/services/http/src/core.rs +57 -35
- data/core/services/http/src/reader.rs +5 -2
- data/core/services/ipfs/Cargo.toml +1 -1
- data/core/services/ipfs/src/backend.rs +20 -9
- data/core/services/ipfs/src/core.rs +43 -34
- data/core/services/ipfs/src/reader.rs +5 -2
- data/core/services/ipmfs/Cargo.toml +1 -1
- data/core/services/ipmfs/src/backend.rs +17 -8
- data/core/services/ipmfs/src/core.rs +66 -60
- data/core/services/ipmfs/src/deleter.rs +5 -2
- data/core/services/ipmfs/src/lister.rs +15 -12
- data/core/services/ipmfs/src/reader.rs +5 -2
- data/core/services/ipmfs/src/writer.rs +6 -3
- data/core/services/koofr/Cargo.toml +2 -2
- data/core/services/koofr/src/backend.rs +46 -17
- data/core/services/koofr/src/core.rs +64 -48
- data/core/services/koofr/src/deleter.rs +4 -1
- data/core/services/koofr/src/lister.rs +11 -10
- data/core/services/koofr/src/reader.rs +5 -2
- data/core/services/koofr/src/writer.rs +13 -7
- data/core/services/lakefs/Cargo.toml +1 -1
- data/core/services/lakefs/src/backend.rs +25 -8
- data/core/services/lakefs/src/core.rs +79 -73
- data/core/services/lakefs/src/deleter.rs +4 -1
- data/core/services/lakefs/src/lister.rs +17 -14
- data/core/services/lakefs/src/reader.rs +5 -2
- data/core/services/lakefs/src/writer.rs +11 -10
- data/core/services/memcached/Cargo.toml +1 -1
- data/core/services/memcached/src/backend.rs +57 -7
- data/core/services/memcached/src/config.rs +13 -2
- data/core/services/memcached/src/core.rs +11 -1
- data/core/services/memcached/src/reader.rs +4 -1
- data/core/services/memcached/src/writer.rs +6 -2
- data/core/services/mini_moka/Cargo.toml +1 -1
- data/core/services/mini_moka/src/backend.rs +8 -8
- data/core/services/mini_moka/src/reader.rs +4 -1
- data/core/services/mini_moka/src/writer.rs +11 -7
- data/core/services/moka/Cargo.toml +1 -1
- data/core/services/moka/src/backend.rs +7 -7
- data/core/services/moka/src/reader.rs +4 -1
- data/core/services/moka/src/writer.rs +10 -6
- data/core/services/mongodb/Cargo.toml +2 -2
- data/core/services/mongodb/src/backend.rs +7 -6
- data/core/services/mongodb/src/core.rs +1 -1
- data/core/services/mongodb/src/reader.rs +4 -1
- data/core/services/mongodb/src/writer.rs +6 -2
- data/core/services/monoiofs/Cargo.toml +2 -2
- data/core/services/monoiofs/src/backend.rs +16 -10
- data/core/services/monoiofs/src/core.rs +8 -9
- data/core/services/monoiofs/src/reader.rs +8 -10
- data/core/services/monoiofs/src/writer.rs +19 -24
- data/core/services/mysql/Cargo.toml +2 -2
- data/core/services/mysql/src/backend.rs +7 -6
- data/core/services/mysql/src/core.rs +1 -1
- data/core/services/mysql/src/lister.rs +7 -3
- data/core/services/mysql/src/reader.rs +4 -1
- data/core/services/mysql/src/writer.rs +6 -2
- data/core/services/obs/Cargo.toml +1 -1
- data/core/services/obs/src/backend.rs +29 -11
- data/core/services/obs/src/core.rs +81 -82
- data/core/services/obs/src/deleter.rs +4 -1
- data/core/services/obs/src/lister.rs +12 -5
- data/core/services/obs/src/reader.rs +5 -2
- data/core/services/obs/src/writer.rs +40 -19
- data/core/services/onedrive/Cargo.toml +2 -2
- data/core/services/onedrive/src/backend.rs +30 -21
- data/core/services/onedrive/src/config.rs +2 -2
- data/core/services/onedrive/src/core.rs +191 -90
- data/core/services/onedrive/src/deleter.rs +5 -2
- data/core/services/onedrive/src/graph_model.rs +10 -51
- data/core/services/onedrive/src/lister.rs +60 -24
- data/core/services/onedrive/src/reader.rs +17 -2
- data/core/services/onedrive/src/writer.rs +26 -15
- data/core/services/opfs/Cargo.toml +1 -1
- data/core/services/opfs/src/backend.rs +6 -13
- data/core/services/opfs/src/core.rs +33 -37
- data/core/services/opfs/src/lister.rs +4 -4
- data/core/services/opfs/src/writer.rs +1 -3
- data/core/services/oss/Cargo.toml +1 -1
- data/core/services/oss/src/backend.rs +40 -8
- data/core/services/oss/src/core.rs +192 -92
- data/core/services/oss/src/deleter.rs +16 -6
- data/core/services/oss/src/lister.rs +43 -27
- data/core/services/oss/src/reader.rs +6 -2
- data/core/services/oss/src/writer.rs +40 -22
- data/core/services/pcloud/Cargo.toml +1 -1
- data/core/services/pcloud/src/backend.rs +40 -6
- data/core/services/pcloud/src/core.rs +80 -66
- data/core/services/pcloud/src/deleter.rs +8 -1
- data/core/services/pcloud/src/lister.rs +4 -1
- data/core/services/pcloud/src/reader.rs +5 -2
- data/core/services/pcloud/src/writer.rs +6 -3
- data/core/services/persy/Cargo.toml +1 -1
- data/core/services/persy/src/backend.rs +6 -5
- data/core/services/persy/src/core.rs +60 -15
- data/core/services/persy/src/reader.rs +4 -1
- data/core/services/persy/src/writer.rs +6 -2
- data/core/services/postgresql/Cargo.toml +2 -2
- data/core/services/postgresql/src/backend.rs +7 -6
- data/core/services/postgresql/src/core.rs +1 -1
- data/core/services/postgresql/src/reader.rs +4 -1
- data/core/services/postgresql/src/writer.rs +6 -2
- data/core/services/redb/Cargo.toml +1 -1
- data/core/services/redb/src/backend.rs +6 -5
- data/core/services/redb/src/reader.rs +4 -1
- data/core/services/redb/src/writer.rs +6 -2
- data/core/services/redis/Cargo.toml +1 -1
- data/core/services/redis/src/backend.rs +18 -8
- data/core/services/redis/src/config.rs +13 -2
- data/core/services/redis/src/reader.rs +8 -4
- data/core/services/redis/src/writer.rs +6 -2
- data/core/services/rocksdb/Cargo.toml +1 -1
- data/core/services/rocksdb/src/backend.rs +6 -5
- data/core/services/rocksdb/src/lister.rs +6 -5
- data/core/services/rocksdb/src/reader.rs +4 -1
- data/core/services/rocksdb/src/writer.rs +6 -2
- data/core/services/s3/Cargo.toml +5 -4
- data/core/services/s3/src/backend.rs +995 -39
- data/core/services/s3/src/compatible_services.md +57 -0
- data/core/services/s3/src/config.rs +16 -0
- data/core/services/s3/src/copier.rs +190 -55
- data/core/services/s3/src/core.rs +299 -190
- data/core/services/s3/src/deleter.rs +26 -7
- data/core/services/s3/src/docs.md +57 -0
- data/core/services/s3/src/lister.rs +55 -36
- data/core/services/s3/src/reader.rs +22 -2
- data/core/services/s3/src/writer.rs +154 -32
- data/core/services/seafile/Cargo.toml +2 -2
- data/core/services/seafile/src/backend.rs +3 -3
- data/core/services/seafile/src/core.rs +105 -79
- data/core/services/seafile/src/lister.rs +11 -8
- data/core/services/seafile/src/reader.rs +5 -2
- data/core/services/seafile/src/writer.rs +6 -3
- data/core/services/sftp/Cargo.toml +1 -1
- data/core/services/sftp/src/backend.rs +19 -4
- data/core/services/sftp/src/core.rs +51 -46
- data/core/services/sftp/src/lister.rs +4 -4
- data/core/services/sftp/src/writer.rs +1 -1
- data/core/services/sled/Cargo.toml +1 -1
- data/core/services/sled/src/backend.rs +6 -5
- data/core/services/sled/src/core.rs +28 -6
- data/core/services/sled/src/lister.rs +6 -5
- data/core/services/sled/src/reader.rs +4 -1
- data/core/services/sled/src/writer.rs +6 -2
- data/core/services/sqlite/Cargo.toml +2 -2
- data/core/services/sqlite/src/backend.rs +12 -7
- data/core/services/sqlite/src/core.rs +3 -3
- data/core/services/sqlite/src/reader.rs +4 -1
- data/core/services/sqlite/src/writer.rs +7 -3
- data/core/services/surrealdb/Cargo.toml +2 -2
- data/core/services/surrealdb/src/backend.rs +7 -6
- data/core/services/surrealdb/src/core.rs +1 -1
- data/core/services/surrealdb/src/reader.rs +4 -1
- data/core/services/surrealdb/src/writer.rs +6 -2
- data/core/services/swift/Cargo.toml +1 -1
- data/core/services/swift/src/backend.rs +38 -8
- data/core/services/swift/src/core.rs +63 -67
- data/core/services/swift/src/deleter.rs +8 -2
- data/core/services/swift/src/lister.rs +11 -8
- data/core/services/swift/src/reader.rs +5 -2
- data/core/services/swift/src/writer.rs +17 -8
- data/core/services/tikv/Cargo.toml +2 -2
- data/core/services/tikv/src/backend.rs +7 -6
- data/core/services/tikv/src/core.rs +31 -14
- data/core/services/tikv/src/reader.rs +4 -1
- data/core/services/tikv/src/writer.rs +6 -2
- data/core/services/tos/Cargo.toml +2 -2
- data/core/services/tos/src/backend.rs +45 -21
- data/core/services/tos/src/copier.rs +44 -17
- data/core/services/tos/src/core.rs +147 -117
- data/core/services/tos/src/deleter.rs +9 -3
- data/core/services/tos/src/docs.md +1 -1
- data/core/services/tos/src/lister.rs +43 -28
- data/core/services/tos/src/reader.rs +5 -2
- data/core/services/tos/src/writer.rs +37 -16
- data/core/services/upyun/Cargo.toml +1 -1
- data/core/services/upyun/src/backend.rs +30 -7
- data/core/services/upyun/src/core.rs +122 -87
- data/core/services/upyun/src/deleter.rs +4 -1
- data/core/services/upyun/src/lister.rs +10 -10
- data/core/services/upyun/src/reader.rs +5 -2
- data/core/services/upyun/src/writer.rs +19 -7
- data/core/services/vercel-artifacts/Cargo.toml +1 -1
- data/core/services/vercel-artifacts/src/backend.rs +6 -3
- data/core/services/vercel-artifacts/src/core.rs +32 -27
- data/core/services/vercel-artifacts/src/reader.rs +5 -2
- data/core/services/vercel-artifacts/src/writer.rs +6 -3
- data/core/services/vercel-blob/Cargo.toml +1 -1
- data/core/services/vercel-blob/src/backend.rs +23 -6
- data/core/services/vercel-blob/src/core.rs +92 -81
- data/core/services/vercel-blob/src/lister.rs +12 -3
- data/core/services/vercel-blob/src/reader.rs +5 -2
- data/core/services/vercel-blob/src/writer.rs +19 -7
- data/core/services/webdav/Cargo.toml +2 -2
- data/core/services/webdav/src/backend.rs +25 -5
- data/core/services/webdav/src/core.rs +116 -59
- data/core/services/webdav/src/deleter.rs +4 -1
- data/core/services/webdav/src/lister.rs +4 -1
- data/core/services/webdav/src/reader.rs +6 -2
- data/core/services/webdav/src/writer.rs +17 -7
- data/core/services/webhdfs/Cargo.toml +2 -2
- data/core/services/webhdfs/src/backend.rs +24 -11
- data/core/services/webhdfs/src/core.rs +99 -98
- data/core/services/webhdfs/src/deleter.rs +5 -2
- data/core/services/webhdfs/src/lister.rs +21 -9
- data/core/services/webhdfs/src/reader.rs +5 -2
- data/core/services/webhdfs/src/writer.rs +49 -14
- data/core/services/yandex-disk/Cargo.toml +1 -1
- data/core/services/yandex-disk/src/backend.rs +28 -6
- data/core/services/yandex-disk/src/core.rs +97 -79
- data/core/services/yandex-disk/src/deleter.rs +4 -1
- data/core/services/yandex-disk/src/lister.rs +25 -9
- data/core/services/yandex-disk/src/reader.rs +5 -2
- data/core/services/yandex-disk/src/writer.rs +6 -3
- data/core/src/lib.rs +5 -0
- data/core/testkit/Cargo.toml +4 -4
- data/core/testkit/src/utils.rs +53 -8
- data/core/tests/behavior/async_compose.rs +297 -0
- data/core/tests/behavior/async_copy.rs +162 -1
- data/core/tests/behavior/async_delete.rs +364 -3
- data/core/tests/behavior/async_read.rs +86 -0
- data/core/tests/behavior/async_restore.rs +162 -0
- data/core/tests/behavior/async_stat.rs +90 -0
- data/core/tests/behavior/async_write.rs +330 -2
- data/core/tests/behavior/main.rs +4 -0
- data/core/upgrade.md +114 -1
- data/src/capability.rs +0 -1
- data/src/io.rs +0 -1
- data/src/lister.rs +0 -1
- data/src/metadata.rs +0 -1
- data/src/operator_info.rs +0 -1
- metadata +44 -2
|
@@ -45,6 +45,8 @@ impl oio::BatchDelete for S3Deleter {
|
|
|
45
45
|
return Ok(());
|
|
46
46
|
}
|
|
47
47
|
|
|
48
|
+
let error_ctx = ErrorContext::new(ServiceOperation("DeleteObject"))
|
|
49
|
+
.with_caller_condition(args.is_conditional());
|
|
48
50
|
let resp = self.core.s3_delete_object(&self.ctx, &path, &args).await?;
|
|
49
51
|
|
|
50
52
|
let status = resp.status();
|
|
@@ -54,8 +56,12 @@ impl oio::BatchDelete for S3Deleter {
|
|
|
54
56
|
// Allow 404 when deleting a non-existing object
|
|
55
57
|
// This is not a standard behavior, only some s3 alike service like GCS XML API do this.
|
|
56
58
|
// ref: <https://cloud.google.com/storage/docs/xml-api/delete-object>
|
|
59
|
+
StatusCode::NOT_FOUND if args.if_match().is_some() => Err(Error::new(
|
|
60
|
+
ErrorKind::ConditionNotMatch,
|
|
61
|
+
"delete precondition requires a live target",
|
|
62
|
+
)),
|
|
57
63
|
StatusCode::NOT_FOUND => Ok(()),
|
|
58
|
-
_ => Err(parse_error(resp)),
|
|
64
|
+
_ => Err(parse_error(error_ctx, resp)),
|
|
59
65
|
}
|
|
60
66
|
}
|
|
61
67
|
|
|
@@ -64,7 +70,10 @@ impl oio::BatchDelete for S3Deleter {
|
|
|
64
70
|
|
|
65
71
|
let status = resp.status();
|
|
66
72
|
if status != StatusCode::OK {
|
|
67
|
-
return Err(parse_error(
|
|
73
|
+
return Err(parse_error(
|
|
74
|
+
ErrorContext::new(ServiceOperation("DeleteObjects")),
|
|
75
|
+
resp,
|
|
76
|
+
));
|
|
68
77
|
}
|
|
69
78
|
|
|
70
79
|
let bs = resp.into_body();
|
|
@@ -88,9 +97,12 @@ impl oio::BatchDelete for S3Deleter {
|
|
|
88
97
|
.position(|e| e.key == abs_path && e.version_id.as_deref() == op.version())
|
|
89
98
|
{
|
|
90
99
|
let error = errors.swap_remove(idx);
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
100
|
+
let conditional = op.if_match().is_some();
|
|
101
|
+
batched_result.failed.push((
|
|
102
|
+
path,
|
|
103
|
+
op,
|
|
104
|
+
parse_delete_objects_result_error(error, conditional),
|
|
105
|
+
));
|
|
94
106
|
} else {
|
|
95
107
|
batched_result.succeeded.push((path, op));
|
|
96
108
|
}
|
|
@@ -100,9 +112,16 @@ impl oio::BatchDelete for S3Deleter {
|
|
|
100
112
|
}
|
|
101
113
|
}
|
|
102
114
|
|
|
103
|
-
fn parse_delete_objects_result_error(err: DeleteObjectsResultError) -> Error {
|
|
115
|
+
fn parse_delete_objects_result_error(err: DeleteObjectsResultError, conditional: bool) -> Error {
|
|
116
|
+
let error_ctx =
|
|
117
|
+
ErrorContext::new(ServiceOperation("DeleteObjects")).with_caller_condition(conditional);
|
|
104
118
|
let (kind, retryable) =
|
|
105
|
-
parse_s3_error_code(err.code.as_str()).unwrap_or((ErrorKind::Unexpected, false));
|
|
119
|
+
parse_s3_error_code(error_ctx, err.code.as_str()).unwrap_or((ErrorKind::Unexpected, false));
|
|
120
|
+
let kind = if conditional && kind == ErrorKind::NotFound {
|
|
121
|
+
ErrorKind::ConditionNotMatch
|
|
122
|
+
} else {
|
|
123
|
+
kind
|
|
124
|
+
};
|
|
106
125
|
let mut err: Error = Error::new(kind, format!("{err:?}"));
|
|
107
126
|
if retryable {
|
|
108
127
|
err = err.set_temporary();
|
|
@@ -9,18 +9,75 @@ Depending on its configuration and the backing system, this service can expose:
|
|
|
9
9
|
- [x] delete
|
|
10
10
|
- [x] list
|
|
11
11
|
- [x] copy
|
|
12
|
+
- [x] restore
|
|
12
13
|
- [ ] rename
|
|
13
14
|
- [x] presign
|
|
14
15
|
|
|
15
16
|
Inspect the effective capability set with [`opendal_core::Operator::info`] and
|
|
16
17
|
[`opendal_core::OperatorInfo::capability`] after building an operator.
|
|
17
18
|
|
|
19
|
+
## Object restoration
|
|
20
|
+
|
|
21
|
+
S3 restoration requires bucket versioning. [`opendal_core::Operator::restore`]
|
|
22
|
+
removes the current delete marker. Calling it on an already-live object succeeds.
|
|
23
|
+
It does not scan or remove older marker versions. If no live object or current
|
|
24
|
+
delete marker exists, it returns [`opendal_core::ErrorKind::NotFound`].
|
|
25
|
+
|
|
26
|
+
Use [`opendal_core::Operator::restore_with`] to promote a specific version:
|
|
27
|
+
|
|
28
|
+
```rust,no_run
|
|
29
|
+
# use opendal_core::Operator;
|
|
30
|
+
# use opendal_core::Result;
|
|
31
|
+
# async fn restore(op: Operator, version: &str) -> Result<()> {
|
|
32
|
+
op.restore_with("path/to/file")
|
|
33
|
+
.version(version)
|
|
34
|
+
.await?;
|
|
35
|
+
# Ok(())
|
|
36
|
+
# }
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Add `.if_not_exists(true)` when a selected version must not overwrite an
|
|
40
|
+
object recreated by another writer. This condition requires an explicit
|
|
41
|
+
version.
|
|
42
|
+
|
|
18
43
|
## Configuration
|
|
19
44
|
|
|
20
45
|
Use [`crate::S3Config`] for serializable configuration and this builder's
|
|
21
46
|
methods for direct construction. The field and method documentation defines
|
|
22
47
|
accepted values, defaults, and environment interaction.
|
|
23
48
|
|
|
49
|
+
## S3 Express One Zone
|
|
50
|
+
|
|
51
|
+
OpenDAL recognizes valid AWS directory bucket names on AWS endpoints and uses
|
|
52
|
+
the bucket's zonal endpoint automatically. It creates and refreshes `ReadWrite`
|
|
53
|
+
S3 Express sessions through the configured AWS credential provider. No extra
|
|
54
|
+
configuration is required:
|
|
55
|
+
|
|
56
|
+
```rust,no_run
|
|
57
|
+
use opendal_core::Operator;
|
|
58
|
+
use opendal_core::Result;
|
|
59
|
+
use opendal_service_s3::S3;
|
|
60
|
+
|
|
61
|
+
fn build_operator() -> Result<Operator> {
|
|
62
|
+
Operator::new(
|
|
63
|
+
S3::default()
|
|
64
|
+
.bucket("example--usw2-az1--x-s3")
|
|
65
|
+
.region("us-west-2"),
|
|
66
|
+
)
|
|
67
|
+
}
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
This automatic behavior applies only to AWS endpoints. S3-compatible services
|
|
71
|
+
retain the existing IAM authentication behavior.
|
|
72
|
+
|
|
73
|
+
An explicit AWS endpoint must match the bucket's derived Zone, Region, and
|
|
74
|
+
partition. OpenDAL rejects unsupported endpoint variants, including dual-stack
|
|
75
|
+
endpoints, with `ConfigInvalid` instead of falling back to general S3 behavior.
|
|
76
|
+
|
|
77
|
+
OpenDAL uses the source IAM credentials for `CopyObject`, `UploadPartCopy`, and
|
|
78
|
+
presigned requests because these operations do not accept S3 Express session
|
|
79
|
+
credentials.
|
|
80
|
+
|
|
24
81
|
## Temporary security credentials
|
|
25
82
|
|
|
26
83
|
OpenDAL now provides support for S3 temporary security credentials in IAM.
|
|
@@ -22,9 +22,8 @@ use quick_xml::de;
|
|
|
22
22
|
|
|
23
23
|
use crate::core::parse_error;
|
|
24
24
|
use crate::core::*;
|
|
25
|
-
use opendal_core::EntryMode;
|
|
26
25
|
use opendal_core::Error;
|
|
27
|
-
use opendal_core::
|
|
26
|
+
use opendal_core::MetadataBuilder;
|
|
28
27
|
use opendal_core::OperationContext;
|
|
29
28
|
use opendal_core::Result;
|
|
30
29
|
use opendal_core::raw::oio::PageContext;
|
|
@@ -90,7 +89,10 @@ impl oio::PageList for S3ListerV1 {
|
|
|
90
89
|
.await?;
|
|
91
90
|
|
|
92
91
|
if resp.status() != http::StatusCode::OK {
|
|
93
|
-
return Err(parse_error(
|
|
92
|
+
return Err(parse_error(
|
|
93
|
+
ErrorContext::new(ServiceOperation("ListObjects")),
|
|
94
|
+
resp,
|
|
95
|
+
));
|
|
94
96
|
}
|
|
95
97
|
let bs = resp.into_body();
|
|
96
98
|
|
|
@@ -130,7 +132,7 @@ impl oio::PageList for S3ListerV1 {
|
|
|
130
132
|
for prefix in output.common_prefixes {
|
|
131
133
|
let de = oio::Entry::new(
|
|
132
134
|
&build_rel_path(&self.core.root, &prefix.prefix),
|
|
133
|
-
|
|
135
|
+
MetadataBuilder::dir().build(),
|
|
134
136
|
);
|
|
135
137
|
|
|
136
138
|
ctx.entries.push_back(de);
|
|
@@ -142,18 +144,20 @@ impl oio::PageList for S3ListerV1 {
|
|
|
142
144
|
path = "/".to_string();
|
|
143
145
|
}
|
|
144
146
|
|
|
145
|
-
let mut meta =
|
|
146
|
-
|
|
147
|
+
let mut meta = if path.ends_with('/') {
|
|
148
|
+
MetadataBuilder::dir()
|
|
149
|
+
} else {
|
|
150
|
+
MetadataBuilder::file(object.size)
|
|
151
|
+
};
|
|
152
|
+
meta.is_current(Some(true));
|
|
147
153
|
if let Some(etag) = &object.etag {
|
|
148
|
-
meta.
|
|
154
|
+
meta.etag(etag);
|
|
149
155
|
}
|
|
150
|
-
meta.set_content_length(object.size);
|
|
151
|
-
|
|
152
156
|
// object.last_modified provides more precise time that contains
|
|
153
157
|
// nanosecond, let's trim them.
|
|
154
|
-
meta.
|
|
158
|
+
meta.last_modified(object.last_modified.parse::<Timestamp>()?);
|
|
155
159
|
|
|
156
|
-
let de = oio::Entry::with(path, meta);
|
|
160
|
+
let de = oio::Entry::with(path, meta.build());
|
|
157
161
|
ctx.entries.push_back(de);
|
|
158
162
|
}
|
|
159
163
|
|
|
@@ -212,7 +216,10 @@ impl oio::PageList for S3ListerV2 {
|
|
|
212
216
|
.await?;
|
|
213
217
|
|
|
214
218
|
if resp.status() != http::StatusCode::OK {
|
|
215
|
-
return Err(parse_error(
|
|
219
|
+
return Err(parse_error(
|
|
220
|
+
ErrorContext::new(ServiceOperation("ListObjectsV2")),
|
|
221
|
+
resp,
|
|
222
|
+
));
|
|
216
223
|
}
|
|
217
224
|
let bs = resp.into_body();
|
|
218
225
|
|
|
@@ -242,7 +249,7 @@ impl oio::PageList for S3ListerV2 {
|
|
|
242
249
|
for prefix in output.common_prefixes {
|
|
243
250
|
let de = oio::Entry::new(
|
|
244
251
|
&build_rel_path(&self.core.root, &prefix.prefix),
|
|
245
|
-
|
|
252
|
+
MetadataBuilder::dir().build(),
|
|
246
253
|
);
|
|
247
254
|
|
|
248
255
|
ctx.entries.push_back(de);
|
|
@@ -254,18 +261,20 @@ impl oio::PageList for S3ListerV2 {
|
|
|
254
261
|
path = "/".to_string();
|
|
255
262
|
}
|
|
256
263
|
|
|
257
|
-
let mut meta =
|
|
258
|
-
|
|
264
|
+
let mut meta = if path.ends_with('/') {
|
|
265
|
+
MetadataBuilder::dir()
|
|
266
|
+
} else {
|
|
267
|
+
MetadataBuilder::file(object.size)
|
|
268
|
+
};
|
|
269
|
+
meta.is_current(Some(true));
|
|
259
270
|
if let Some(etag) = &object.etag {
|
|
260
|
-
meta.
|
|
271
|
+
meta.etag(etag);
|
|
261
272
|
}
|
|
262
|
-
meta.set_content_length(object.size);
|
|
263
|
-
|
|
264
273
|
// object.last_modified provides more precise time that contains
|
|
265
274
|
// nanosecond, let's trim them.
|
|
266
|
-
meta.
|
|
275
|
+
meta.last_modified(object.last_modified.parse::<Timestamp>()?);
|
|
267
276
|
|
|
268
|
-
let de = oio::Entry::with(path, meta);
|
|
277
|
+
let de = oio::Entry::with(path, meta.build());
|
|
269
278
|
ctx.entries.push_back(de);
|
|
270
279
|
}
|
|
271
280
|
|
|
@@ -326,7 +335,10 @@ impl oio::PageList for S3ObjectVersionsLister {
|
|
|
326
335
|
)
|
|
327
336
|
.await?;
|
|
328
337
|
if resp.status() != http::StatusCode::OK {
|
|
329
|
-
return Err(parse_error(
|
|
338
|
+
return Err(parse_error(
|
|
339
|
+
ErrorContext::new(ServiceOperation("ListObjectVersions")),
|
|
340
|
+
resp,
|
|
341
|
+
));
|
|
330
342
|
}
|
|
331
343
|
|
|
332
344
|
let body = resp.into_body();
|
|
@@ -353,7 +365,7 @@ impl oio::PageList for S3ObjectVersionsLister {
|
|
|
353
365
|
for prefix in output.common_prefixes {
|
|
354
366
|
let de = oio::Entry::new(
|
|
355
367
|
&build_rel_path(&self.core.root, &prefix.prefix),
|
|
356
|
-
|
|
368
|
+
MetadataBuilder::dir().build(),
|
|
357
369
|
);
|
|
358
370
|
ctx.entries.push_back(de);
|
|
359
371
|
}
|
|
@@ -372,17 +384,20 @@ impl oio::PageList for S3ObjectVersionsLister {
|
|
|
372
384
|
path = "/".to_owned();
|
|
373
385
|
}
|
|
374
386
|
|
|
375
|
-
let mut meta =
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
387
|
+
let mut meta = if path.ends_with('/') {
|
|
388
|
+
MetadataBuilder::dir()
|
|
389
|
+
} else {
|
|
390
|
+
MetadataBuilder::file(version_object.size)
|
|
391
|
+
};
|
|
392
|
+
meta.version(&version_object.version_id)
|
|
393
|
+
.is_current(Some(version_object.is_latest))
|
|
394
|
+
.last_modified(version_object.last_modified.parse::<Timestamp>()?);
|
|
380
395
|
|
|
381
396
|
if let Some(etag) = version_object.etag {
|
|
382
|
-
meta.
|
|
397
|
+
meta.etag(&etag);
|
|
383
398
|
}
|
|
384
399
|
|
|
385
|
-
let entry = oio::Entry::new(&path, meta);
|
|
400
|
+
let entry = oio::Entry::new(&path, meta.build());
|
|
386
401
|
ctx.entries.push_back(entry);
|
|
387
402
|
}
|
|
388
403
|
|
|
@@ -393,13 +408,17 @@ impl oio::PageList for S3ObjectVersionsLister {
|
|
|
393
408
|
path = "/".to_owned();
|
|
394
409
|
}
|
|
395
410
|
|
|
396
|
-
let mut meta =
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
411
|
+
let mut meta = if path.ends_with('/') {
|
|
412
|
+
MetadataBuilder::dir()
|
|
413
|
+
} else {
|
|
414
|
+
MetadataBuilder::file(0)
|
|
415
|
+
};
|
|
416
|
+
meta.version(&delete_marker.version_id)
|
|
417
|
+
.is_deleted(true)
|
|
418
|
+
.is_current(Some(delete_marker.is_latest))
|
|
419
|
+
.last_modified(delete_marker.last_modified.parse::<Timestamp>()?);
|
|
420
|
+
|
|
421
|
+
let entry = oio::Entry::new(&path, meta.build());
|
|
403
422
|
ctx.entries.push_back(entry);
|
|
404
423
|
}
|
|
405
424
|
}
|
|
@@ -16,7 +16,11 @@
|
|
|
16
16
|
// under the License.
|
|
17
17
|
|
|
18
18
|
use crate::backend::*;
|
|
19
|
+
use crate::core::ErrorContext;
|
|
20
|
+
use crate::core::constants::X_AMZ_META_PREFIX;
|
|
21
|
+
use crate::core::constants::X_AMZ_VERSION_ID;
|
|
19
22
|
use crate::core::parse_error;
|
|
23
|
+
use http::HeaderMap;
|
|
20
24
|
use http::Response;
|
|
21
25
|
use http::StatusCode;
|
|
22
26
|
use opendal_core::raw::*;
|
|
@@ -48,6 +52,8 @@ impl oio::StreamRead for S3Reader {
|
|
|
48
52
|
let backend = &self.backend;
|
|
49
53
|
let path = self.path.as_str();
|
|
50
54
|
let args = self.args.clone();
|
|
55
|
+
let error_ctx = ErrorContext::new(ServiceOperation("GetObject"))
|
|
56
|
+
.with_caller_condition(args.is_conditional());
|
|
51
57
|
let resp = backend
|
|
52
58
|
.core
|
|
53
59
|
.s3_get_object(&self.ctx, path, range, &args)
|
|
@@ -56,16 +62,30 @@ impl oio::StreamRead for S3Reader {
|
|
|
56
62
|
let status = resp.status();
|
|
57
63
|
let (rp, stream) = match status {
|
|
58
64
|
StatusCode::OK | StatusCode::PARTIAL_CONTENT => (
|
|
59
|
-
RpRead::new(
|
|
65
|
+
RpRead::new(parse_into_s3_metadata(path, resp.headers())?),
|
|
60
66
|
resp.into_body(),
|
|
61
67
|
),
|
|
62
68
|
_ => {
|
|
63
69
|
let (part, mut body) = resp.into_parts();
|
|
64
70
|
let buf = body.to_buffer().await?;
|
|
65
|
-
return Err(parse_error(Response::from_parts(part, buf)));
|
|
71
|
+
return Err(parse_error(error_ctx, Response::from_parts(part, buf)));
|
|
66
72
|
}
|
|
67
73
|
};
|
|
68
74
|
|
|
69
75
|
Ok((rp, Box::new(stream) as Box<dyn oio::ReadStreamDyn>))
|
|
70
76
|
}
|
|
71
77
|
}
|
|
78
|
+
|
|
79
|
+
pub(super) fn parse_into_s3_metadata(path: &str, headers: &HeaderMap) -> Result<Metadata> {
|
|
80
|
+
let mut meta = parse_into_metadata(path, headers)?.into_builder();
|
|
81
|
+
|
|
82
|
+
let user_meta = parse_prefixed_headers(headers, X_AMZ_META_PREFIX);
|
|
83
|
+
if !user_meta.is_empty() {
|
|
84
|
+
meta.user_metadata(user_meta);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
if let Some(v) = parse_header_to_str(headers, X_AMZ_VERSION_ID)? {
|
|
88
|
+
meta.version(v);
|
|
89
|
+
}
|
|
90
|
+
Ok(meta.build())
|
|
91
|
+
}
|
|
@@ -20,10 +20,9 @@ use std::sync::Arc;
|
|
|
20
20
|
use bytes::Buf;
|
|
21
21
|
use constants::X_AMZ_OBJECT_SIZE;
|
|
22
22
|
use constants::X_AMZ_VERSION_ID;
|
|
23
|
+
use http::Response;
|
|
23
24
|
use http::StatusCode;
|
|
24
25
|
|
|
25
|
-
use crate::core::S3Error;
|
|
26
|
-
use crate::core::from_s3_error;
|
|
27
26
|
use crate::core::parse_error;
|
|
28
27
|
use crate::core::*;
|
|
29
28
|
use opendal_core::raw::*;
|
|
@@ -50,19 +49,28 @@ impl S3Writer {
|
|
|
50
49
|
}
|
|
51
50
|
|
|
52
51
|
fn parse_header_into_meta(path: &str, headers: &http::HeaderMap) -> Result<Metadata> {
|
|
53
|
-
let mut meta =
|
|
52
|
+
let mut meta = if path.ends_with('/') {
|
|
53
|
+
MetadataBuilder::dir()
|
|
54
|
+
} else {
|
|
55
|
+
MetadataBuilder::unknown()
|
|
56
|
+
};
|
|
54
57
|
if let Some(etag) = parse_etag(headers)? {
|
|
55
|
-
meta.
|
|
58
|
+
meta.etag(etag);
|
|
56
59
|
}
|
|
57
60
|
if let Some(version) = parse_header_to_str(headers, X_AMZ_VERSION_ID)? {
|
|
58
|
-
meta.
|
|
61
|
+
meta.version(version);
|
|
59
62
|
}
|
|
60
|
-
if
|
|
61
|
-
|
|
63
|
+
if !path.ends_with('/')
|
|
64
|
+
&& let Some(value) =
|
|
65
|
+
parse_header_to_str(headers, X_AMZ_OBJECT_SIZE)?.and_then(|size| size.parse().ok())
|
|
62
66
|
{
|
|
63
|
-
meta.
|
|
67
|
+
meta.set_file(value);
|
|
64
68
|
}
|
|
65
|
-
Ok(meta)
|
|
69
|
+
Ok(meta.build())
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
fn error_context(&self, service_operation: ServiceOperation) -> ErrorContext {
|
|
73
|
+
ErrorContext::new(service_operation).with_caller_condition(self.op.is_conditional())
|
|
66
74
|
}
|
|
67
75
|
}
|
|
68
76
|
|
|
@@ -72,7 +80,10 @@ impl oio::MultipartWrite for S3Writer {
|
|
|
72
80
|
.core
|
|
73
81
|
.s3_put_object_request(&self.path, Some(size), &self.op, body)?;
|
|
74
82
|
|
|
75
|
-
let resp = self
|
|
83
|
+
let resp = self
|
|
84
|
+
.core
|
|
85
|
+
.send(&self.ctx, req, self.core.signers.default())
|
|
86
|
+
.await?;
|
|
76
87
|
|
|
77
88
|
let status = resp.status();
|
|
78
89
|
|
|
@@ -80,7 +91,17 @@ impl oio::MultipartWrite for S3Writer {
|
|
|
80
91
|
|
|
81
92
|
match status {
|
|
82
93
|
StatusCode::CREATED | StatusCode::OK => Ok(meta),
|
|
83
|
-
_ =>
|
|
94
|
+
_ => {
|
|
95
|
+
let err = parse_error(self.error_context(ServiceOperation("PutObject")), resp);
|
|
96
|
+
if self.op.if_match().is_some() && err.kind() == ErrorKind::NotFound {
|
|
97
|
+
Err(Error::new(
|
|
98
|
+
ErrorKind::ConditionNotMatch,
|
|
99
|
+
"write precondition requires a live target",
|
|
100
|
+
))
|
|
101
|
+
} else {
|
|
102
|
+
Err(err)
|
|
103
|
+
}
|
|
104
|
+
}
|
|
84
105
|
}
|
|
85
106
|
}
|
|
86
107
|
|
|
@@ -101,7 +122,10 @@ impl oio::MultipartWrite for S3Writer {
|
|
|
101
122
|
|
|
102
123
|
Ok(result.upload_id)
|
|
103
124
|
}
|
|
104
|
-
_ => Err(parse_error(
|
|
125
|
+
_ => Err(parse_error(
|
|
126
|
+
ErrorContext::new(ServiceOperation("CreateMultipartUpload")),
|
|
127
|
+
resp,
|
|
128
|
+
)),
|
|
105
129
|
}
|
|
106
130
|
}
|
|
107
131
|
|
|
@@ -126,7 +150,10 @@ impl oio::MultipartWrite for S3Writer {
|
|
|
126
150
|
checksum.clone(),
|
|
127
151
|
)?;
|
|
128
152
|
|
|
129
|
-
let resp = self
|
|
153
|
+
let resp = self
|
|
154
|
+
.core
|
|
155
|
+
.send(&self.ctx, req, self.core.signers.default())
|
|
156
|
+
.await?;
|
|
130
157
|
|
|
131
158
|
let status = resp.status();
|
|
132
159
|
|
|
@@ -148,7 +175,70 @@ impl oio::MultipartWrite for S3Writer {
|
|
|
148
175
|
size: None,
|
|
149
176
|
})
|
|
150
177
|
}
|
|
151
|
-
_ => Err(parse_error(
|
|
178
|
+
_ => Err(parse_error(
|
|
179
|
+
ErrorContext::new(ServiceOperation("UploadPart")),
|
|
180
|
+
resp,
|
|
181
|
+
)),
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
async fn copy_part(
|
|
186
|
+
&self,
|
|
187
|
+
upload_id: &str,
|
|
188
|
+
part_number: usize,
|
|
189
|
+
path: &str,
|
|
190
|
+
args: OpRead,
|
|
191
|
+
range: BytesRange,
|
|
192
|
+
) -> Result<oio::MultipartPart> {
|
|
193
|
+
let size = range
|
|
194
|
+
.size()
|
|
195
|
+
.expect("multipart writer copy range must be bounded");
|
|
196
|
+
let part_number = part_number + 1;
|
|
197
|
+
let error_context = ErrorContext::new(ServiceOperation("UploadPartCopy"))
|
|
198
|
+
.with_caller_condition(args.is_conditional());
|
|
199
|
+
let req = self
|
|
200
|
+
.core
|
|
201
|
+
.s3_upload_part_copy_request(S3UploadPartCopyRequest {
|
|
202
|
+
from: path,
|
|
203
|
+
to: &self.path,
|
|
204
|
+
source_version: args.version(),
|
|
205
|
+
if_match: args.if_match(),
|
|
206
|
+
if_none_match: args.if_none_match(),
|
|
207
|
+
if_modified_since: args.if_modified_since(),
|
|
208
|
+
if_unmodified_since: args.if_unmodified_since(),
|
|
209
|
+
upload_id,
|
|
210
|
+
part_number,
|
|
211
|
+
range,
|
|
212
|
+
operation: Operation::Write,
|
|
213
|
+
})?;
|
|
214
|
+
|
|
215
|
+
let resp = self
|
|
216
|
+
.core
|
|
217
|
+
.send(&self.ctx, req, self.core.signers.iam())
|
|
218
|
+
.await?;
|
|
219
|
+
match resp.status() {
|
|
220
|
+
StatusCode::OK => {
|
|
221
|
+
let (parts, body) = resp.into_parts();
|
|
222
|
+
let bs = body.to_bytes();
|
|
223
|
+
let result: CopyObjectResult =
|
|
224
|
+
quick_xml::de::from_reader(bs.as_ref()).map_err(new_xml_deserialize_error)?;
|
|
225
|
+
|
|
226
|
+
// S3 may return 200 OK with an <Error> body for UploadPartCopy.
|
|
227
|
+
if result.etag.is_empty() {
|
|
228
|
+
return Err(parse_error(
|
|
229
|
+
error_context,
|
|
230
|
+
Response::from_parts(parts, Buffer::from(bs)),
|
|
231
|
+
));
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
Ok(oio::MultipartPart {
|
|
235
|
+
part_number,
|
|
236
|
+
etag: result.etag,
|
|
237
|
+
checksum: None,
|
|
238
|
+
size: Some(size),
|
|
239
|
+
})
|
|
240
|
+
}
|
|
241
|
+
_ => Err(parse_error(error_context, resp)),
|
|
152
242
|
}
|
|
153
243
|
}
|
|
154
244
|
|
|
@@ -187,32 +277,50 @@ impl oio::MultipartWrite for S3Writer {
|
|
|
187
277
|
|
|
188
278
|
let status = resp.status();
|
|
189
279
|
|
|
190
|
-
let
|
|
280
|
+
let meta = S3Writer::parse_header_into_meta(&self.path, resp.headers())?;
|
|
191
281
|
|
|
192
282
|
match status {
|
|
193
283
|
StatusCode::OK => {
|
|
194
284
|
// still check if there is any error because S3 might return error for status code 200
|
|
195
285
|
// https://docs.aws.amazon.com/AmazonS3/latest/API/API_CompleteMultipartUpload.html#API_CompleteMultipartUpload_Example_4
|
|
196
286
|
let (parts, body) = resp.into_parts();
|
|
287
|
+
let bs = body.to_bytes();
|
|
197
288
|
|
|
198
289
|
let ret: CompleteMultipartUploadResult =
|
|
199
|
-
quick_xml::de::from_reader(
|
|
290
|
+
quick_xml::de::from_reader(bs.as_ref()).map_err(new_xml_deserialize_error)?;
|
|
200
291
|
if !ret.code.is_empty() {
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
292
|
+
let err = parse_error(
|
|
293
|
+
self.error_context(ServiceOperation("CompleteMultipartUpload")),
|
|
294
|
+
Response::from_parts(parts, Buffer::from(bs)),
|
|
295
|
+
);
|
|
296
|
+
return if self.op.if_match().is_some() && err.kind() == ErrorKind::NotFound {
|
|
297
|
+
Err(Error::new(
|
|
298
|
+
ErrorKind::ConditionNotMatch,
|
|
299
|
+
"write precondition requires a live target",
|
|
300
|
+
))
|
|
301
|
+
} else {
|
|
302
|
+
Err(err)
|
|
303
|
+
};
|
|
210
304
|
}
|
|
211
|
-
meta.
|
|
305
|
+
let mut meta = meta.into_builder();
|
|
306
|
+
meta.etag(&ret.etag);
|
|
212
307
|
|
|
213
|
-
Ok(meta)
|
|
308
|
+
Ok(meta.build())
|
|
309
|
+
}
|
|
310
|
+
_ => {
|
|
311
|
+
let err = parse_error(
|
|
312
|
+
self.error_context(ServiceOperation("CompleteMultipartUpload")),
|
|
313
|
+
resp,
|
|
314
|
+
);
|
|
315
|
+
if self.op.if_match().is_some() && err.kind() == ErrorKind::NotFound {
|
|
316
|
+
Err(Error::new(
|
|
317
|
+
ErrorKind::ConditionNotMatch,
|
|
318
|
+
"write precondition requires a live target",
|
|
319
|
+
))
|
|
320
|
+
} else {
|
|
321
|
+
Err(err)
|
|
322
|
+
}
|
|
214
323
|
}
|
|
215
|
-
_ => Err(parse_error(resp)),
|
|
216
324
|
}
|
|
217
325
|
}
|
|
218
326
|
|
|
@@ -224,7 +332,10 @@ impl oio::MultipartWrite for S3Writer {
|
|
|
224
332
|
match resp.status() {
|
|
225
333
|
// s3 returns code 204 if abort succeeds.
|
|
226
334
|
StatusCode::NO_CONTENT => Ok(()),
|
|
227
|
-
_ => Err(parse_error(
|
|
335
|
+
_ => Err(parse_error(
|
|
336
|
+
ErrorContext::new(ServiceOperation("AbortMultipartUpload")),
|
|
337
|
+
resp,
|
|
338
|
+
)),
|
|
228
339
|
}
|
|
229
340
|
}
|
|
230
341
|
}
|
|
@@ -241,16 +352,27 @@ impl oio::AppendWrite for S3Writer {
|
|
|
241
352
|
match status {
|
|
242
353
|
StatusCode::OK => Ok(parse_content_length(resp.headers())?.unwrap_or_default()),
|
|
243
354
|
StatusCode::NOT_FOUND => Ok(0),
|
|
244
|
-
_ => Err(parse_error(
|
|
355
|
+
_ => Err(parse_error(
|
|
356
|
+
ErrorContext::new(ServiceOperation("HeadObject")),
|
|
357
|
+
resp,
|
|
358
|
+
)),
|
|
245
359
|
}
|
|
246
360
|
}
|
|
247
361
|
|
|
248
362
|
async fn append(&self, offset: u64, size: u64, body: Buffer) -> Result<Metadata> {
|
|
363
|
+
let error_ctx = if offset == 0 {
|
|
364
|
+
self.error_context(ServiceOperation("PutObject"))
|
|
365
|
+
} else {
|
|
366
|
+
ErrorContext::new(ServiceOperation("PutObject"))
|
|
367
|
+
};
|
|
249
368
|
let req = self
|
|
250
369
|
.core
|
|
251
370
|
.s3_append_object_request(&self.path, offset, size, &self.op, body)?;
|
|
252
371
|
|
|
253
|
-
let resp = self
|
|
372
|
+
let resp = self
|
|
373
|
+
.core
|
|
374
|
+
.send(&self.ctx, req, self.core.signers.default())
|
|
375
|
+
.await?;
|
|
254
376
|
|
|
255
377
|
let status = resp.status();
|
|
256
378
|
|
|
@@ -258,7 +380,7 @@ impl oio::AppendWrite for S3Writer {
|
|
|
258
380
|
|
|
259
381
|
match status {
|
|
260
382
|
StatusCode::CREATED | StatusCode::OK => Ok(meta),
|
|
261
|
-
_ => Err(parse_error(resp)),
|
|
383
|
+
_ => Err(parse_error(error_ctx, resp)),
|
|
262
384
|
}
|
|
263
385
|
}
|
|
264
386
|
}
|
|
@@ -32,12 +32,12 @@ version = { workspace = true }
|
|
|
32
32
|
all-features = true
|
|
33
33
|
|
|
34
34
|
[dependencies]
|
|
35
|
-
opendal-core = { path = "../../core", version = "0.
|
|
35
|
+
opendal-core = { path = "../../core", version = "0.59.0", default-features = false }
|
|
36
36
|
|
|
37
|
+
asyncband = { workspace = true }
|
|
37
38
|
bytes = { workspace = true }
|
|
38
39
|
http = { workspace = true }
|
|
39
40
|
log = { workspace = true }
|
|
40
|
-
mea = { workspace = true }
|
|
41
41
|
serde = { workspace = true, features = ["derive"] }
|
|
42
42
|
serde_json = { workspace = true }
|
|
43
43
|
|