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.
Files changed (566) hide show
  1. checksums.yaml +4 -4
  2. data/Cargo.lock +89 -56
  3. data/Cargo.toml +1 -1
  4. data/DEPENDENCIES.rust.tsv +55 -41
  5. data/LICENSE +201 -0
  6. data/LICENSE-MPL-2.0.txt +373 -0
  7. data/NOTICE +5 -0
  8. data/core/CHANGELOG.md +175 -0
  9. data/core/Cargo.lock +225 -298
  10. data/core/Cargo.toml +100 -94
  11. data/core/DEPENDENCIES.rust.tsv +44 -73
  12. data/core/benches/metadata/README.md +21 -0
  13. data/core/benches/metadata/main.rs +541 -0
  14. data/core/benches/vs_fs/Cargo.toml +1 -1
  15. data/core/benches/vs_s3/Cargo.toml +1 -1
  16. data/core/core/Cargo.toml +7 -2
  17. data/core/core/src/blocking/delete.rs +5 -1
  18. data/core/core/src/blocking/operator.rs +12 -0
  19. data/core/core/src/blocking/read/std_reader.rs +25 -0
  20. data/core/core/src/docs/concepts.rs +70 -94
  21. data/core/core/src/docs/internals/accessor.rs +62 -293
  22. data/core/core/src/docs/internals/layer.rs +43 -59
  23. data/core/core/src/docs/internals/mod.rs +21 -23
  24. data/core/core/src/docs/mod.rs +10 -6
  25. data/core/core/src/docs/performance/concurrent_write.md +92 -55
  26. data/core/core/src/docs/performance/http_optimization.md +120 -104
  27. data/core/core/src/docs/performance/mod.rs +8 -6
  28. data/core/core/src/docs/rfcs/5485_conditional_reader.md +4 -0
  29. data/core/core/src/docs/rfcs/7182_restore_api.md +139 -557
  30. data/core/core/src/docs/rfcs/7945_limited_read.md +210 -0
  31. data/core/core/src/docs/rfcs/7948_s3_provider_presets.md +254 -0
  32. data/core/core/src/docs/rfcs/8109_writer_copy_from.md +330 -0
  33. data/core/core/src/docs/rfcs/8145_conflict_error_kind.md +210 -0
  34. data/core/core/src/docs/rfcs/8147_write_if_not_changed.md +233 -0
  35. data/core/core/src/docs/rfcs/8188_compose_api.md +292 -0
  36. data/core/core/src/docs/rfcs/8194_compact_metadata_and_operation_args.md +444 -0
  37. data/core/core/src/docs/rfcs/README.md +14 -3
  38. data/core/core/src/docs/rfcs/mod.rs +46 -22
  39. data/core/core/src/docs/specs/README.md +34 -0
  40. data/core/core/src/docs/specs/conditional-operations.md +156 -0
  41. data/core/core/src/docs/specs/metadata.md +63 -0
  42. data/core/core/src/docs/specs/mod.rs +26 -0
  43. data/core/core/src/docs/upgrade.md +114 -1
  44. data/core/core/src/layers/capability_override.rs +15 -2
  45. data/core/core/src/layers/complete.rs +347 -43
  46. data/core/core/src/layers/correctness_check.rs +718 -51
  47. data/core/core/src/layers/error_context.rs +76 -2
  48. data/core/core/src/layers/simulate.rs +91 -28
  49. data/core/core/src/lib.rs +1 -0
  50. data/core/core/src/raw/accessor.rs +91 -15
  51. data/core/core/src/raw/enum_utils.rs +7 -0
  52. data/core/core/src/raw/http_util/header.rs +22 -17
  53. data/core/core/src/raw/oio/compose/api.rs +92 -0
  54. data/core/core/src/raw/oio/compose/mod.rs +19 -0
  55. data/core/core/src/raw/oio/copy/api.rs +3 -3
  56. data/core/core/src/raw/oio/copy/block_copy.rs +32 -4
  57. data/core/core/src/raw/oio/copy/multipart_copy.rs +36 -7
  58. data/core/core/src/raw/oio/entry.rs +48 -22
  59. data/core/core/src/raw/oio/list/flat_list.rs +1 -1
  60. data/core/core/src/raw/oio/list/hierarchy_list.rs +3 -2
  61. data/core/core/src/raw/oio/mod.rs +3 -0
  62. data/core/core/src/raw/oio/read/position_read.rs +1 -1
  63. data/core/core/src/raw/oio/write/api.rs +40 -0
  64. data/core/core/src/raw/oio/write/append_write.rs +4 -4
  65. data/core/core/src/raw/oio/write/block_write.rs +2 -2
  66. data/core/core/src/raw/oio/write/multipart_write.rs +181 -48
  67. data/core/core/src/raw/oio/write/position_write.rs +1 -1
  68. data/core/core/src/raw/operation.rs +6 -0
  69. data/core/core/src/raw/ops.rs +1191 -456
  70. data/core/core/src/raw/rps.rs +11 -0
  71. data/core/core/src/raw/time.rs +14 -3
  72. data/core/core/src/services/memory/backend.rs +5 -10
  73. data/core/core/src/services/memory/lister.rs +4 -4
  74. data/core/core/src/services/memory/writer.rs +7 -6
  75. data/core/core/src/types/buffer.rs +102 -12
  76. data/core/core/src/types/bytes_range.rs +20 -1
  77. data/core/core/src/types/capability.rs +65 -0
  78. data/core/core/src/types/compact.rs +329 -0
  79. data/core/core/src/types/compose/composer.rs +183 -0
  80. data/core/core/src/types/compose/input.rs +57 -0
  81. data/core/core/src/types/compose/mod.rs +23 -0
  82. data/core/core/src/types/context/read.rs +16 -11
  83. data/core/core/src/types/context/write.rs +150 -2
  84. data/core/core/src/types/copy.rs +5 -3
  85. data/core/core/src/types/delete/deleter.rs +20 -12
  86. data/core/core/src/types/delete/input.rs +22 -64
  87. data/core/core/src/types/delete/mod.rs +0 -1
  88. data/core/core/src/types/entry.rs +8 -14
  89. data/core/core/src/types/error.rs +28 -11
  90. data/core/core/src/types/metadata.rs +709 -322
  91. data/core/core/src/types/mod.rs +9 -1
  92. data/core/core/src/types/operator/operator.rs +278 -29
  93. data/core/core/src/types/operator/operator_futures.rs +466 -105
  94. data/core/core/src/types/options.rs +631 -158
  95. data/core/core/src/types/read/futures_async_reader.rs +663 -50
  96. data/core/core/src/types/read/reader.rs +23 -5
  97. data/core/core/src/types/write/futures_async_writer.rs +27 -0
  98. data/core/core/src/types/write/writer.rs +47 -5
  99. data/core/http-transports/reqwest/Cargo.toml +1 -1
  100. data/core/layers/async-backtrace/Cargo.toml +2 -2
  101. data/core/layers/async-backtrace/src/lib.rs +21 -2
  102. data/core/layers/await-tree/Cargo.toml +2 -2
  103. data/core/layers/await-tree/src/lib.rs +25 -2
  104. data/core/layers/capability-check/Cargo.toml +2 -2
  105. data/core/layers/capability-check/src/lib.rs +98 -10
  106. data/core/layers/chaos/Cargo.toml +2 -2
  107. data/core/layers/chaos/src/lib.rs +18 -3
  108. data/core/layers/concurrent-limit/Cargo.toml +3 -3
  109. data/core/layers/concurrent-limit/src/lib.rs +53 -13
  110. data/core/layers/dtrace/Cargo.toml +2 -2
  111. data/core/layers/dtrace/src/lib.rs +29 -2
  112. data/core/layers/fastmetrics/Cargo.toml +3 -3
  113. data/core/layers/fastrace/Cargo.toml +2 -2
  114. data/core/layers/fastrace/src/lib.rs +28 -2
  115. data/core/layers/foyer/Cargo.toml +2 -2
  116. data/core/layers/foyer/src/full.rs +16 -19
  117. data/core/layers/foyer/src/lib.rs +67 -14
  118. data/core/layers/hotpath/Cargo.toml +2 -2
  119. data/core/layers/hotpath/src/lib.rs +20 -2
  120. data/core/layers/immutable-index/Cargo.toml +2 -2
  121. data/core/layers/immutable-index/src/lib.rs +48 -24
  122. data/core/layers/logging/Cargo.toml +2 -2
  123. data/core/layers/logging/src/lib.rs +46 -2
  124. data/core/layers/metrics/Cargo.toml +3 -3
  125. data/core/layers/mime-guess/Cargo.toml +2 -2
  126. data/core/layers/mime-guess/src/lib.rs +31 -4
  127. data/core/layers/observe-metrics-common/Cargo.toml +2 -2
  128. data/core/layers/observe-metrics-common/src/lib.rs +55 -2
  129. data/core/layers/otelmetrics/Cargo.toml +3 -3
  130. data/core/layers/oteltrace/Cargo.toml +2 -2
  131. data/core/layers/oteltrace/src/lib.rs +27 -2
  132. data/core/layers/prometheus/Cargo.toml +3 -3
  133. data/core/layers/prometheus-client/Cargo.toml +3 -3
  134. data/core/layers/retry/Cargo.toml +4 -4
  135. data/core/layers/retry/src/lib.rs +76 -19
  136. data/core/layers/route/Cargo.toml +3 -3
  137. data/core/layers/route/src/lib.rs +34 -7
  138. data/core/layers/tail-cut/Cargo.toml +2 -2
  139. data/core/layers/tail-cut/src/lib.rs +28 -2
  140. data/core/layers/throttle/Cargo.toml +2 -2
  141. data/core/layers/throttle/src/lib.rs +19 -2
  142. data/core/layers/timeout/Cargo.toml +3 -3
  143. data/core/layers/timeout/src/lib.rs +38 -13
  144. data/core/layers/tracing/Cargo.toml +2 -2
  145. data/core/layers/tracing/src/lib.rs +24 -3
  146. data/core/services/aliyun-drive/Cargo.toml +2 -2
  147. data/core/services/aliyun-drive/src/backend.rs +34 -24
  148. data/core/services/aliyun-drive/src/core.rs +143 -54
  149. data/core/services/aliyun-drive/src/lister.rs +19 -18
  150. data/core/services/aliyun-drive/src/reader.rs +4 -1
  151. data/core/services/aliyun-drive/src/writer.rs +3 -3
  152. data/core/services/alluxio/Cargo.toml +1 -1
  153. data/core/services/alluxio/src/backend.rs +1 -1
  154. data/core/services/alluxio/src/core.rs +113 -94
  155. data/core/services/alluxio/src/reader.rs +7 -3
  156. data/core/services/alluxio/src/writer.rs +2 -2
  157. data/core/services/azblob/Cargo.toml +2 -2
  158. data/core/services/azblob/src/backend.rs +17 -9
  159. data/core/services/azblob/src/copier.rs +41 -15
  160. data/core/services/azblob/src/core.rs +372 -135
  161. data/core/services/azblob/src/deleter.rs +41 -18
  162. data/core/services/azblob/src/lister.rs +16 -10
  163. data/core/services/azblob/src/reader.rs +4 -1
  164. data/core/services/azblob/src/writer.rs +48 -15
  165. data/core/services/azdls/Cargo.toml +3 -3
  166. data/core/services/azdls/src/backend.rs +45 -15
  167. data/core/services/azdls/src/config.rs +4 -1
  168. data/core/services/azdls/src/core.rs +276 -87
  169. data/core/services/azdls/src/deleter.rs +13 -4
  170. data/core/services/azdls/src/lister.rs +18 -12
  171. data/core/services/azdls/src/reader.rs +6 -2
  172. data/core/services/azdls/src/writer.rs +44 -23
  173. data/core/services/azfile/Cargo.toml +2 -2
  174. data/core/services/azfile/src/backend.rs +24 -8
  175. data/core/services/azfile/src/core.rs +195 -77
  176. data/core/services/azfile/src/deleter.rs +8 -1
  177. data/core/services/azfile/src/lister.rs +20 -14
  178. data/core/services/azfile/src/reader.rs +5 -2
  179. data/core/services/azfile/src/writer.rs +27 -15
  180. data/core/services/azure-common/Cargo.toml +1 -1
  181. data/core/services/b2/Cargo.toml +2 -2
  182. data/core/services/b2/src/backend.rs +30 -11
  183. data/core/services/b2/src/core.rs +134 -120
  184. data/core/services/b2/src/deleter.rs +1 -1
  185. data/core/services/b2/src/lister.rs +5 -2
  186. data/core/services/b2/src/reader.rs +5 -2
  187. data/core/services/b2/src/writer.rs +29 -12
  188. data/core/services/cacache/Cargo.toml +1 -1
  189. data/core/services/cacache/src/backend.rs +4 -5
  190. data/core/services/cacache/src/reader.rs +4 -1
  191. data/core/services/cacache/src/writer.rs +6 -2
  192. data/core/services/cloudflare-kv/Cargo.toml +1 -1
  193. data/core/services/cloudflare-kv/src/backend.rs +26 -15
  194. data/core/services/cloudflare-kv/src/config.rs +13 -2
  195. data/core/services/cloudflare-kv/src/core.rs +62 -56
  196. data/core/services/cloudflare-kv/src/deleter.rs +12 -3
  197. data/core/services/cloudflare-kv/src/lister.rs +66 -17
  198. data/core/services/cloudflare-kv/src/reader.rs +14 -9
  199. data/core/services/cloudflare-kv/src/writer.rs +10 -7
  200. data/core/services/compfs/Cargo.toml +1 -1
  201. data/core/services/compfs/src/backend.rs +11 -7
  202. data/core/services/compfs/src/lister.rs +7 -7
  203. data/core/services/compfs/src/writer.rs +1 -1
  204. data/core/services/cos/Cargo.toml +1 -1
  205. data/core/services/cos/src/backend.rs +129 -16
  206. data/core/services/cos/src/core.rs +198 -80
  207. data/core/services/cos/src/deleter.rs +4 -1
  208. data/core/services/cos/src/lister.rs +42 -27
  209. data/core/services/cos/src/reader.rs +6 -2
  210. data/core/services/cos/src/writer.rs +45 -21
  211. data/core/services/d1/Cargo.toml +1 -1
  212. data/core/services/d1/src/backend.rs +6 -5
  213. data/core/services/d1/src/core.rs +79 -63
  214. data/core/services/d1/src/reader.rs +4 -1
  215. data/core/services/d1/src/writer.rs +6 -2
  216. data/core/services/dashmap/Cargo.toml +1 -1
  217. data/core/services/dashmap/src/backend.rs +2 -2
  218. data/core/services/dashmap/src/reader.rs +4 -1
  219. data/core/services/dashmap/src/writer.rs +11 -8
  220. data/core/services/dbfs/Cargo.toml +1 -1
  221. data/core/services/dbfs/src/backend.rs +21 -15
  222. data/core/services/dbfs/src/core.rs +65 -60
  223. data/core/services/dbfs/src/deleter.rs +4 -1
  224. data/core/services/dbfs/src/lister.rs +10 -10
  225. data/core/services/dbfs/src/writer.rs +6 -3
  226. data/core/services/dropbox/Cargo.toml +2 -2
  227. data/core/services/dropbox/src/backend.rs +30 -23
  228. data/core/services/dropbox/src/core.rs +65 -63
  229. data/core/services/dropbox/src/deleter.rs +1 -1
  230. data/core/services/dropbox/src/lister.rs +17 -8
  231. data/core/services/dropbox/src/reader.rs +4 -1
  232. data/core/services/dropbox/src/writer.rs +13 -10
  233. data/core/services/etcd/Cargo.toml +1 -1
  234. data/core/services/etcd/src/backend.rs +8 -5
  235. data/core/services/etcd/src/core.rs +5 -11
  236. data/core/services/etcd/src/lister.rs +6 -1
  237. data/core/services/etcd/src/reader.rs +4 -1
  238. data/core/services/etcd/src/writer.rs +6 -3
  239. data/core/services/foundationdb/Cargo.toml +1 -1
  240. data/core/services/foundationdb/src/backend.rs +6 -5
  241. data/core/services/foundationdb/src/reader.rs +4 -1
  242. data/core/services/foundationdb/src/writer.rs +6 -2
  243. data/core/services/foyer/Cargo.toml +3 -3
  244. data/core/services/foyer/src/backend.rs +6 -5
  245. data/core/services/foyer/src/reader.rs +4 -1
  246. data/core/services/foyer/src/writer.rs +6 -2
  247. data/core/services/fs/Cargo.toml +1 -1
  248. data/core/services/fs/src/backend.rs +4 -3
  249. data/core/services/fs/src/core.rs +44 -26
  250. data/core/services/fs/src/lister.rs +9 -6
  251. data/core/services/fs/src/writer.rs +20 -13
  252. data/core/services/ftp/Cargo.toml +4 -6
  253. data/core/services/ftp/src/backend.rs +9 -5
  254. data/core/services/ftp/src/core.rs +9 -7
  255. data/core/services/ftp/src/lister.rs +7 -8
  256. data/core/services/ftp/src/reader.rs +3 -3
  257. data/core/services/ftp/src/writer.rs +3 -3
  258. data/core/services/gcs/Cargo.toml +4 -2
  259. data/core/services/gcs/src/backend.rs +53 -7
  260. data/core/services/gcs/src/composer.rs +705 -0
  261. data/core/services/gcs/src/copier.rs +14 -4
  262. data/core/services/gcs/src/core.rs +828 -124
  263. data/core/services/gcs/src/deleter.rs +33 -18
  264. data/core/services/gcs/src/lib.rs +1 -0
  265. data/core/services/gcs/src/lister.rs +21 -12
  266. data/core/services/gcs/src/reader.rs +12 -5
  267. data/core/services/gcs/src/writer.rs +632 -10
  268. data/core/services/gcs-grpc/Cargo.toml +58 -0
  269. data/core/services/gcs-grpc/README.md +19 -0
  270. data/core/services/gcs-grpc/proto/README.md +23 -0
  271. data/core/services/gcs-grpc/proto/google/storage/v2/storage.proto +171 -0
  272. data/core/services/gcs-grpc/proto/upstream-descriptor.bin +0 -0
  273. data/core/services/gcs-grpc/src/backend.rs +449 -0
  274. data/core/services/gcs-grpc/src/config.rs +116 -0
  275. data/core/services/gcs-grpc/src/copier.rs +98 -0
  276. data/core/services/gcs-grpc/src/core.rs +298 -0
  277. data/core/services/gcs-grpc/src/deleter.rs +65 -0
  278. data/core/services/gcs-grpc/src/docs.md +19 -0
  279. data/core/services/gcs-grpc/src/generated/google.storage.v2.rs +541 -0
  280. data/core/services/gcs-grpc/src/generated/mod.rs +24 -0
  281. data/core/services/gcs-grpc/src/lib.rs +43 -0
  282. data/core/services/gcs-grpc/src/lister.rs +125 -0
  283. data/core/services/gcs-grpc/src/reader.rs +140 -0
  284. data/core/services/gcs-grpc/src/writer.rs +780 -0
  285. data/core/services/gdrive/Cargo.toml +2 -2
  286. data/core/services/gdrive/src/backend.rs +123 -69
  287. data/core/services/gdrive/src/core.rs +87 -69
  288. data/core/services/gdrive/src/deleter.rs +8 -2
  289. data/core/services/gdrive/src/lister.rs +38 -26
  290. data/core/services/gdrive/src/path_index.rs +2 -2
  291. data/core/services/gdrive/src/reader.rs +9 -3
  292. data/core/services/gdrive/src/writer.rs +13 -7
  293. data/core/services/ghac/Cargo.toml +2 -2
  294. data/core/services/ghac/src/backend.rs +5 -2
  295. data/core/services/ghac/src/core.rs +59 -37
  296. data/core/services/ghac/src/reader.rs +5 -2
  297. data/core/services/ghac/src/writer.rs +16 -6
  298. data/core/services/github/Cargo.toml +1 -1
  299. data/core/services/github/src/backend.rs +16 -8
  300. data/core/services/github/src/core.rs +88 -76
  301. data/core/services/github/src/lister.rs +17 -15
  302. data/core/services/github/src/reader.rs +5 -2
  303. data/core/services/github/src/writer.rs +12 -6
  304. data/core/services/goosefs/Cargo.toml +2 -2
  305. data/core/services/goosefs/src/backend.rs +2 -2
  306. data/core/services/goosefs/src/core.rs +291 -208
  307. data/core/services/goosefs/src/docs.md +3 -0
  308. data/core/services/goosefs/src/lister.rs +2 -3
  309. data/core/services/goosefs/src/reader.rs +7 -3
  310. data/core/services/goosefs/src/writer.rs +21 -10
  311. data/core/services/goosefs/tests/safe_conditional_put.rs +37 -0
  312. data/core/services/gridfs/Cargo.toml +2 -2
  313. data/core/services/gridfs/src/backend.rs +12 -8
  314. data/core/services/gridfs/src/core.rs +1 -1
  315. data/core/services/gridfs/src/reader.rs +4 -1
  316. data/core/services/gridfs/src/writer.rs +6 -2
  317. data/core/services/hdfs/Cargo.toml +1 -1
  318. data/core/services/hdfs/src/backend.rs +1 -1
  319. data/core/services/hdfs/src/core.rs +7 -4
  320. data/core/services/hdfs/src/lister.rs +7 -9
  321. data/core/services/hdfs/src/writer.rs +5 -1
  322. data/core/services/hdfs-native/Cargo.toml +1 -1
  323. data/core/services/hdfs-native/src/backend.rs +1 -1
  324. data/core/services/hdfs-native/src/core.rs +44 -50
  325. data/core/services/hdfs-native/src/lister.rs +8 -10
  326. data/core/services/hdfs-native/src/writer.rs +5 -1
  327. data/core/services/hf/Cargo.toml +4 -3
  328. data/core/services/hf/src/backend.rs +4 -3
  329. data/core/services/hf/src/core.rs +907 -153
  330. data/core/services/hf/src/lister.rs +16 -6
  331. data/core/services/hf/src/reader.rs +377 -57
  332. data/core/services/hf/src/writer.rs +6 -2
  333. data/core/services/http/Cargo.toml +3 -3
  334. data/core/services/http/src/backend.rs +8 -6
  335. data/core/services/http/src/core.rs +57 -35
  336. data/core/services/http/src/reader.rs +5 -2
  337. data/core/services/ipfs/Cargo.toml +1 -1
  338. data/core/services/ipfs/src/backend.rs +20 -9
  339. data/core/services/ipfs/src/core.rs +43 -34
  340. data/core/services/ipfs/src/reader.rs +5 -2
  341. data/core/services/ipmfs/Cargo.toml +1 -1
  342. data/core/services/ipmfs/src/backend.rs +17 -8
  343. data/core/services/ipmfs/src/core.rs +66 -60
  344. data/core/services/ipmfs/src/deleter.rs +5 -2
  345. data/core/services/ipmfs/src/lister.rs +15 -12
  346. data/core/services/ipmfs/src/reader.rs +5 -2
  347. data/core/services/ipmfs/src/writer.rs +6 -3
  348. data/core/services/koofr/Cargo.toml +2 -2
  349. data/core/services/koofr/src/backend.rs +46 -17
  350. data/core/services/koofr/src/core.rs +64 -48
  351. data/core/services/koofr/src/deleter.rs +4 -1
  352. data/core/services/koofr/src/lister.rs +11 -10
  353. data/core/services/koofr/src/reader.rs +5 -2
  354. data/core/services/koofr/src/writer.rs +13 -7
  355. data/core/services/lakefs/Cargo.toml +1 -1
  356. data/core/services/lakefs/src/backend.rs +25 -8
  357. data/core/services/lakefs/src/core.rs +79 -73
  358. data/core/services/lakefs/src/deleter.rs +4 -1
  359. data/core/services/lakefs/src/lister.rs +17 -14
  360. data/core/services/lakefs/src/reader.rs +5 -2
  361. data/core/services/lakefs/src/writer.rs +11 -10
  362. data/core/services/memcached/Cargo.toml +1 -1
  363. data/core/services/memcached/src/backend.rs +57 -7
  364. data/core/services/memcached/src/config.rs +13 -2
  365. data/core/services/memcached/src/core.rs +11 -1
  366. data/core/services/memcached/src/reader.rs +4 -1
  367. data/core/services/memcached/src/writer.rs +6 -2
  368. data/core/services/mini_moka/Cargo.toml +1 -1
  369. data/core/services/mini_moka/src/backend.rs +8 -8
  370. data/core/services/mini_moka/src/reader.rs +4 -1
  371. data/core/services/mini_moka/src/writer.rs +11 -7
  372. data/core/services/moka/Cargo.toml +1 -1
  373. data/core/services/moka/src/backend.rs +7 -7
  374. data/core/services/moka/src/reader.rs +4 -1
  375. data/core/services/moka/src/writer.rs +10 -6
  376. data/core/services/mongodb/Cargo.toml +2 -2
  377. data/core/services/mongodb/src/backend.rs +7 -6
  378. data/core/services/mongodb/src/core.rs +1 -1
  379. data/core/services/mongodb/src/reader.rs +4 -1
  380. data/core/services/mongodb/src/writer.rs +6 -2
  381. data/core/services/monoiofs/Cargo.toml +2 -2
  382. data/core/services/monoiofs/src/backend.rs +16 -10
  383. data/core/services/monoiofs/src/core.rs +8 -9
  384. data/core/services/monoiofs/src/reader.rs +8 -10
  385. data/core/services/monoiofs/src/writer.rs +19 -24
  386. data/core/services/mysql/Cargo.toml +2 -2
  387. data/core/services/mysql/src/backend.rs +7 -6
  388. data/core/services/mysql/src/core.rs +1 -1
  389. data/core/services/mysql/src/lister.rs +7 -3
  390. data/core/services/mysql/src/reader.rs +4 -1
  391. data/core/services/mysql/src/writer.rs +6 -2
  392. data/core/services/obs/Cargo.toml +1 -1
  393. data/core/services/obs/src/backend.rs +29 -11
  394. data/core/services/obs/src/core.rs +81 -82
  395. data/core/services/obs/src/deleter.rs +4 -1
  396. data/core/services/obs/src/lister.rs +12 -5
  397. data/core/services/obs/src/reader.rs +5 -2
  398. data/core/services/obs/src/writer.rs +40 -19
  399. data/core/services/onedrive/Cargo.toml +2 -2
  400. data/core/services/onedrive/src/backend.rs +30 -21
  401. data/core/services/onedrive/src/config.rs +2 -2
  402. data/core/services/onedrive/src/core.rs +191 -90
  403. data/core/services/onedrive/src/deleter.rs +5 -2
  404. data/core/services/onedrive/src/graph_model.rs +10 -51
  405. data/core/services/onedrive/src/lister.rs +60 -24
  406. data/core/services/onedrive/src/reader.rs +17 -2
  407. data/core/services/onedrive/src/writer.rs +26 -15
  408. data/core/services/opfs/Cargo.toml +1 -1
  409. data/core/services/opfs/src/backend.rs +6 -13
  410. data/core/services/opfs/src/core.rs +33 -37
  411. data/core/services/opfs/src/lister.rs +4 -4
  412. data/core/services/opfs/src/writer.rs +1 -3
  413. data/core/services/oss/Cargo.toml +1 -1
  414. data/core/services/oss/src/backend.rs +40 -8
  415. data/core/services/oss/src/core.rs +192 -92
  416. data/core/services/oss/src/deleter.rs +16 -6
  417. data/core/services/oss/src/lister.rs +43 -27
  418. data/core/services/oss/src/reader.rs +6 -2
  419. data/core/services/oss/src/writer.rs +40 -22
  420. data/core/services/pcloud/Cargo.toml +1 -1
  421. data/core/services/pcloud/src/backend.rs +40 -6
  422. data/core/services/pcloud/src/core.rs +80 -66
  423. data/core/services/pcloud/src/deleter.rs +8 -1
  424. data/core/services/pcloud/src/lister.rs +4 -1
  425. data/core/services/pcloud/src/reader.rs +5 -2
  426. data/core/services/pcloud/src/writer.rs +6 -3
  427. data/core/services/persy/Cargo.toml +1 -1
  428. data/core/services/persy/src/backend.rs +6 -5
  429. data/core/services/persy/src/core.rs +60 -15
  430. data/core/services/persy/src/reader.rs +4 -1
  431. data/core/services/persy/src/writer.rs +6 -2
  432. data/core/services/postgresql/Cargo.toml +2 -2
  433. data/core/services/postgresql/src/backend.rs +7 -6
  434. data/core/services/postgresql/src/core.rs +1 -1
  435. data/core/services/postgresql/src/reader.rs +4 -1
  436. data/core/services/postgresql/src/writer.rs +6 -2
  437. data/core/services/redb/Cargo.toml +1 -1
  438. data/core/services/redb/src/backend.rs +6 -5
  439. data/core/services/redb/src/reader.rs +4 -1
  440. data/core/services/redb/src/writer.rs +6 -2
  441. data/core/services/redis/Cargo.toml +1 -1
  442. data/core/services/redis/src/backend.rs +18 -8
  443. data/core/services/redis/src/config.rs +13 -2
  444. data/core/services/redis/src/reader.rs +8 -4
  445. data/core/services/redis/src/writer.rs +6 -2
  446. data/core/services/rocksdb/Cargo.toml +1 -1
  447. data/core/services/rocksdb/src/backend.rs +6 -5
  448. data/core/services/rocksdb/src/lister.rs +6 -5
  449. data/core/services/rocksdb/src/reader.rs +4 -1
  450. data/core/services/rocksdb/src/writer.rs +6 -2
  451. data/core/services/s3/Cargo.toml +5 -4
  452. data/core/services/s3/src/backend.rs +995 -39
  453. data/core/services/s3/src/compatible_services.md +57 -0
  454. data/core/services/s3/src/config.rs +16 -0
  455. data/core/services/s3/src/copier.rs +190 -55
  456. data/core/services/s3/src/core.rs +299 -190
  457. data/core/services/s3/src/deleter.rs +26 -7
  458. data/core/services/s3/src/docs.md +57 -0
  459. data/core/services/s3/src/lister.rs +55 -36
  460. data/core/services/s3/src/reader.rs +22 -2
  461. data/core/services/s3/src/writer.rs +154 -32
  462. data/core/services/seafile/Cargo.toml +2 -2
  463. data/core/services/seafile/src/backend.rs +3 -3
  464. data/core/services/seafile/src/core.rs +105 -79
  465. data/core/services/seafile/src/lister.rs +11 -8
  466. data/core/services/seafile/src/reader.rs +5 -2
  467. data/core/services/seafile/src/writer.rs +6 -3
  468. data/core/services/sftp/Cargo.toml +1 -1
  469. data/core/services/sftp/src/backend.rs +19 -4
  470. data/core/services/sftp/src/core.rs +51 -46
  471. data/core/services/sftp/src/lister.rs +4 -4
  472. data/core/services/sftp/src/writer.rs +1 -1
  473. data/core/services/sled/Cargo.toml +1 -1
  474. data/core/services/sled/src/backend.rs +6 -5
  475. data/core/services/sled/src/core.rs +28 -6
  476. data/core/services/sled/src/lister.rs +6 -5
  477. data/core/services/sled/src/reader.rs +4 -1
  478. data/core/services/sled/src/writer.rs +6 -2
  479. data/core/services/sqlite/Cargo.toml +2 -2
  480. data/core/services/sqlite/src/backend.rs +12 -7
  481. data/core/services/sqlite/src/core.rs +3 -3
  482. data/core/services/sqlite/src/reader.rs +4 -1
  483. data/core/services/sqlite/src/writer.rs +7 -3
  484. data/core/services/surrealdb/Cargo.toml +2 -2
  485. data/core/services/surrealdb/src/backend.rs +7 -6
  486. data/core/services/surrealdb/src/core.rs +1 -1
  487. data/core/services/surrealdb/src/reader.rs +4 -1
  488. data/core/services/surrealdb/src/writer.rs +6 -2
  489. data/core/services/swift/Cargo.toml +1 -1
  490. data/core/services/swift/src/backend.rs +38 -8
  491. data/core/services/swift/src/core.rs +63 -67
  492. data/core/services/swift/src/deleter.rs +8 -2
  493. data/core/services/swift/src/lister.rs +11 -8
  494. data/core/services/swift/src/reader.rs +5 -2
  495. data/core/services/swift/src/writer.rs +17 -8
  496. data/core/services/tikv/Cargo.toml +2 -2
  497. data/core/services/tikv/src/backend.rs +7 -6
  498. data/core/services/tikv/src/core.rs +31 -14
  499. data/core/services/tikv/src/reader.rs +4 -1
  500. data/core/services/tikv/src/writer.rs +6 -2
  501. data/core/services/tos/Cargo.toml +2 -2
  502. data/core/services/tos/src/backend.rs +45 -21
  503. data/core/services/tos/src/copier.rs +44 -17
  504. data/core/services/tos/src/core.rs +147 -117
  505. data/core/services/tos/src/deleter.rs +9 -3
  506. data/core/services/tos/src/docs.md +1 -1
  507. data/core/services/tos/src/lister.rs +43 -28
  508. data/core/services/tos/src/reader.rs +5 -2
  509. data/core/services/tos/src/writer.rs +37 -16
  510. data/core/services/upyun/Cargo.toml +1 -1
  511. data/core/services/upyun/src/backend.rs +30 -7
  512. data/core/services/upyun/src/core.rs +122 -87
  513. data/core/services/upyun/src/deleter.rs +4 -1
  514. data/core/services/upyun/src/lister.rs +10 -10
  515. data/core/services/upyun/src/reader.rs +5 -2
  516. data/core/services/upyun/src/writer.rs +19 -7
  517. data/core/services/vercel-artifacts/Cargo.toml +1 -1
  518. data/core/services/vercel-artifacts/src/backend.rs +6 -3
  519. data/core/services/vercel-artifacts/src/core.rs +32 -27
  520. data/core/services/vercel-artifacts/src/reader.rs +5 -2
  521. data/core/services/vercel-artifacts/src/writer.rs +6 -3
  522. data/core/services/vercel-blob/Cargo.toml +1 -1
  523. data/core/services/vercel-blob/src/backend.rs +23 -6
  524. data/core/services/vercel-blob/src/core.rs +92 -81
  525. data/core/services/vercel-blob/src/lister.rs +12 -3
  526. data/core/services/vercel-blob/src/reader.rs +5 -2
  527. data/core/services/vercel-blob/src/writer.rs +19 -7
  528. data/core/services/webdav/Cargo.toml +2 -2
  529. data/core/services/webdav/src/backend.rs +25 -5
  530. data/core/services/webdav/src/core.rs +116 -59
  531. data/core/services/webdav/src/deleter.rs +4 -1
  532. data/core/services/webdav/src/lister.rs +4 -1
  533. data/core/services/webdav/src/reader.rs +6 -2
  534. data/core/services/webdav/src/writer.rs +17 -7
  535. data/core/services/webhdfs/Cargo.toml +2 -2
  536. data/core/services/webhdfs/src/backend.rs +24 -11
  537. data/core/services/webhdfs/src/core.rs +99 -98
  538. data/core/services/webhdfs/src/deleter.rs +5 -2
  539. data/core/services/webhdfs/src/lister.rs +21 -9
  540. data/core/services/webhdfs/src/reader.rs +5 -2
  541. data/core/services/webhdfs/src/writer.rs +49 -14
  542. data/core/services/yandex-disk/Cargo.toml +1 -1
  543. data/core/services/yandex-disk/src/backend.rs +28 -6
  544. data/core/services/yandex-disk/src/core.rs +97 -79
  545. data/core/services/yandex-disk/src/deleter.rs +4 -1
  546. data/core/services/yandex-disk/src/lister.rs +25 -9
  547. data/core/services/yandex-disk/src/reader.rs +5 -2
  548. data/core/services/yandex-disk/src/writer.rs +6 -3
  549. data/core/src/lib.rs +5 -0
  550. data/core/testkit/Cargo.toml +4 -4
  551. data/core/testkit/src/utils.rs +53 -8
  552. data/core/tests/behavior/async_compose.rs +297 -0
  553. data/core/tests/behavior/async_copy.rs +162 -1
  554. data/core/tests/behavior/async_delete.rs +364 -3
  555. data/core/tests/behavior/async_read.rs +86 -0
  556. data/core/tests/behavior/async_restore.rs +162 -0
  557. data/core/tests/behavior/async_stat.rs +90 -0
  558. data/core/tests/behavior/async_write.rs +330 -2
  559. data/core/tests/behavior/main.rs +4 -0
  560. data/core/upgrade.md +114 -1
  561. data/src/capability.rs +0 -1
  562. data/src/io.rs +0 -1
  563. data/src/lister.rs +0 -1
  564. data/src/metadata.rs +0 -1
  565. data/src/operator_info.rs +0 -1
  566. 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(resp));
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
- batched_result
92
- .failed
93
- .push((path, op, parse_delete_objects_result_error(error)));
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::Metadata;
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(resp));
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
- Metadata::new(EntryMode::DIR),
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 = Metadata::new(EntryMode::from_path(&path));
146
- meta.set_is_current(true);
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.set_etag(etag);
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.set_last_modified(object.last_modified.parse::<Timestamp>()?);
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(resp));
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
- Metadata::new(EntryMode::DIR),
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 = Metadata::new(EntryMode::from_path(&path));
258
- meta.set_is_current(true);
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.set_etag(etag);
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.set_last_modified(object.last_modified.parse::<Timestamp>()?);
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(resp));
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
- Metadata::new(EntryMode::DIR),
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 = Metadata::new(EntryMode::from_path(&path));
376
- meta.set_version(&version_object.version_id);
377
- meta.set_is_current(version_object.is_latest);
378
- meta.set_content_length(version_object.size);
379
- meta.set_last_modified(version_object.last_modified.parse::<Timestamp>()?);
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.set_etag(&etag);
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 = Metadata::new(EntryMode::from_path(&path));
397
- meta.set_version(&delete_marker.version_id);
398
- meta.set_is_deleted(true);
399
- meta.set_is_current(delete_marker.is_latest);
400
- meta.set_last_modified(delete_marker.last_modified.parse::<Timestamp>()?);
401
-
402
- let entry = oio::Entry::new(&path, meta);
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(parse_into_metadata(path, resp.headers())?),
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 = Metadata::new(EntryMode::from_path(path));
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.set_etag(etag);
58
+ meta.etag(etag);
56
59
  }
57
60
  if let Some(version) = parse_header_to_str(headers, X_AMZ_VERSION_ID)? {
58
- meta.set_version(version);
61
+ meta.version(version);
59
62
  }
60
- if let Some(value) =
61
- parse_header_to_str(headers, X_AMZ_OBJECT_SIZE)?.and_then(|size| size.parse().ok())
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.set_content_length(value);
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.core.send(&self.ctx, req).await?;
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
- _ => Err(parse_error(resp)),
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(resp)),
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.core.send(&self.ctx, req).await?;
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(resp)),
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 mut meta = S3Writer::parse_header_into_meta(&self.path, resp.headers())?;
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(body.reader()).map_err(new_xml_deserialize_error)?;
290
+ quick_xml::de::from_reader(bs.as_ref()).map_err(new_xml_deserialize_error)?;
200
291
  if !ret.code.is_empty() {
201
- return Err(from_s3_error(
202
- S3Error {
203
- code: ret.code,
204
- message: ret.message,
205
- resource: "".to_string(),
206
- request_id: ret.request_id,
207
- },
208
- parts,
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.set_etag(&ret.etag);
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(resp)),
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(resp)),
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.core.send(&self.ctx, req).await?;
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.58.1", default-features = false }
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