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
@@ -240,13 +240,13 @@ impl OssCore {
240
240
  ///
241
241
  /// before return the user defined metadata, we'll strip the user_metadata_prefix from the key
242
242
  pub fn parse_metadata(&self, path: &str, headers: &HeaderMap) -> Result<Metadata> {
243
- let mut m = parse_into_metadata(path, headers)?;
243
+ let mut m = parse_into_metadata(path, headers)?.into_builder();
244
244
  let user_meta = parse_prefixed_headers(headers, X_OSS_META_PREFIX);
245
245
  if !user_meta.is_empty() {
246
- m = m.with_user_metadata(user_meta);
246
+ m.user_metadata(user_meta);
247
247
  }
248
248
 
249
- Ok(m)
249
+ Ok(m.build())
250
250
  }
251
251
  }
252
252
 
@@ -657,33 +657,36 @@ impl OssCore {
657
657
  }
658
658
  }
659
659
 
660
- #[allow(clippy::too_many_arguments)]
661
660
  pub async fn oss_initiate_upload(
662
661
  &self,
663
662
  ctx: &OperationContext,
664
663
  path: &str,
665
- content_type: Option<&str>,
666
- content_disposition: Option<&str>,
667
- cache_control: Option<&str>,
668
- content_encoding: Option<&str>,
664
+ args: &OpWrite,
669
665
  is_presign: bool,
670
666
  ) -> Result<Response<Buffer>> {
671
667
  let path = build_abs_path(&self.root, path);
672
668
  let endpoint = self.get_endpoint(is_presign);
673
669
  let url = format!("{}/{}?uploads", endpoint, percent_encode_path(&path));
674
670
  let mut req = Request::post(&url);
675
- if let Some(mime) = content_type {
671
+ if let Some(mime) = args.content_type() {
676
672
  req = req.header(CONTENT_TYPE, mime);
677
673
  }
678
- if let Some(disposition) = content_disposition {
674
+ if let Some(disposition) = args.content_disposition() {
679
675
  req = req.header(CONTENT_DISPOSITION, disposition);
680
676
  }
681
- if let Some(cache_control) = cache_control {
677
+ if let Some(cache_control) = args.cache_control() {
682
678
  req = req.header(CACHE_CONTROL, cache_control);
683
679
  }
684
- if let Some(encoding) = content_encoding {
680
+ if let Some(encoding) = args.content_encoding() {
685
681
  req = req.header(CONTENT_ENCODING, encoding);
686
682
  }
683
+ // OSS evaluates x-oss-forbid-overwrite on both InitiateMultipartUpload and
684
+ // CompleteMultipartUpload. Setting it only on one of them is not enough.
685
+ //
686
+ // ref: https://www.alibabacloud.com/help/en/oss/developer-reference/initiatemultipartupload
687
+ if args.if_not_exists() {
688
+ req = req.header(X_OSS_FORBID_OVERWRITE, "true");
689
+ }
687
690
  req = self.insert_sse_headers(req);
688
691
 
689
692
  let req = req
@@ -737,6 +740,7 @@ impl OssCore {
737
740
  upload_id: &str,
738
741
  is_presign: bool,
739
742
  parts: Vec<MultipartUploadPart>,
743
+ args: &OpWrite,
740
744
  ) -> Result<Response<Buffer>> {
741
745
  let p = build_abs_path(&self.root, path);
742
746
  let endpoint = self.get_endpoint(is_presign);
@@ -747,16 +751,23 @@ impl OssCore {
747
751
  percent_encode_path(upload_id)
748
752
  );
749
753
 
750
- let req = Request::post(&url);
754
+ let mut req = Request::post(&url);
751
755
 
752
756
  let content = quick_xml::se::to_string(&CompleteMultipartUploadRequest {
753
757
  part: parts.to_vec(),
754
758
  })
755
759
  .map_err(new_xml_serialize_error)?;
756
760
  // Make sure content length has been set to avoid post with chunked encoding.
757
- let req = req.header(CONTENT_LENGTH, content.len());
761
+ req = req.header(CONTENT_LENGTH, content.len());
758
762
  // Set content-type to `application/xml` to avoid mixed with form post.
759
- let req = req.header(CONTENT_TYPE, "application/xml");
763
+ req = req.header(CONTENT_TYPE, "application/xml");
764
+ // CompleteMultipartUpload is the request that commits the object, so
765
+ // if_not_exists must also be enforced here.
766
+ //
767
+ // ref: https://www.alibabacloud.com/help/en/oss/developer-reference/completemultipartupload
768
+ if args.if_not_exists() {
769
+ req = req.header(X_OSS_FORBID_OVERWRITE, "true");
770
+ }
760
771
 
761
772
  let req = req
762
773
  .extension(Operation::Write)
@@ -1133,71 +1144,12 @@ mod tests {
1133
1144
  ]
1134
1145
  )
1135
1146
  }
1136
- }
1137
-
1138
- mod error {
1139
- use bytes::Buf;
1140
- use http::Response;
1141
- use http::StatusCode;
1142
- use quick_xml::de;
1143
- use serde::Deserialize;
1144
-
1145
- use opendal_core::raw::*;
1146
- use opendal_core::*;
1147
-
1148
- /// OssError is the error returned by oss service.
1149
- #[derive(Default, Debug, Deserialize)]
1150
- #[serde(default, rename_all = "PascalCase")]
1151
- struct OssError {
1152
- code: String,
1153
- message: String,
1154
- request_id: String,
1155
- host_id: String,
1156
- }
1157
-
1158
- /// Parse error response into Error.
1159
- pub(crate) fn parse_error(resp: Response<Buffer>) -> Error {
1160
- let (parts, body) = resp.into_parts();
1161
- let bs = body.to_bytes();
1162
1147
 
1163
- let (kind, retryable) = match parts.status {
1164
- StatusCode::NOT_FOUND => (ErrorKind::NotFound, false),
1165
- StatusCode::FORBIDDEN => (ErrorKind::PermissionDenied, false),
1166
- StatusCode::PRECONDITION_FAILED | StatusCode::NOT_MODIFIED | StatusCode::CONFLICT => {
1167
- (ErrorKind::ConditionNotMatch, false)
1168
- }
1169
- StatusCode::INTERNAL_SERVER_ERROR
1170
- | StatusCode::BAD_GATEWAY
1171
- | StatusCode::SERVICE_UNAVAILABLE
1172
- | StatusCode::GATEWAY_TIMEOUT => (ErrorKind::Unexpected, true),
1173
- _ => (ErrorKind::Unexpected, false),
1174
- };
1175
-
1176
- let message = match de::from_reader::<_, OssError>(bs.clone().reader()) {
1177
- Ok(oss_err) => format!("{oss_err:?}"),
1178
- Err(_) => String::from_utf8_lossy(&bs).into_owned(),
1179
- };
1180
-
1181
- let mut err = Error::new(kind, message);
1182
-
1183
- err = with_error_response_context(err, parts);
1184
-
1185
- if retryable {
1186
- err = err.set_temporary();
1187
- }
1188
-
1189
- err
1190
- }
1191
-
1192
- #[cfg(test)]
1193
- mod tests {
1194
- use super::*;
1195
-
1196
- /// Error response example is from https://www.alibabacloud.com/help/en/object-storage-service/latest/error-responses
1197
- #[test]
1198
- fn test_parse_error() {
1199
- let bs = bytes::Bytes::from(
1200
- r#"
1148
+ /// Error response example is from https://www.alibabacloud.com/help/en/object-storage-service/latest/error-responses
1149
+ #[test]
1150
+ fn test_parse_error() {
1151
+ let bs = bytes::Bytes::from(
1152
+ r#"
1201
1153
  <?xml version="1.0" ?>
1202
1154
  <Error xmlns="http://doc.oss-cn-hangzhou.aliyuncs.com">
1203
1155
  <Code>
@@ -1214,20 +1166,168 @@ mod error {
1214
1166
  </HostId>
1215
1167
  </Error>
1216
1168
  "#,
1217
- );
1218
-
1219
- let out: OssError = de::from_reader(bs.reader()).expect("must success");
1220
- println!("{out:?}");
1221
-
1222
- assert_eq!(out.code.trim(), "AccessDenied");
1223
- assert_eq!(
1224
- out.message.trim(),
1225
- "Query-string authentication requires the Signature, Expires and OSSAccessKeyId parameters"
1226
- );
1227
- assert_eq!(out.request_id.trim(), "1D842BC54255****");
1228
- assert_eq!(out.host_id.trim(), "oss-cn-hangzhou.aliyuncs.com");
1169
+ );
1170
+
1171
+ let out: OssError = de::from_reader(bs.reader()).expect("must success");
1172
+ println!("{out:?}");
1173
+
1174
+ assert_eq!(out.code.trim(), "AccessDenied");
1175
+ assert_eq!(
1176
+ out.message.trim(),
1177
+ "Query-string authentication requires the Signature, Expires and OSSAccessKeyId parameters"
1178
+ );
1179
+ assert_eq!(out.request_id.trim(), "1D842BC54255****");
1180
+ assert_eq!(out.host_id.trim(), "oss-cn-hangzhou.aliyuncs.com");
1181
+ }
1182
+ }
1183
+
1184
+ use bytes::Buf;
1185
+ use http::StatusCode;
1186
+ use quick_xml::de;
1187
+
1188
+ /// OssError is the error returned by oss service.
1189
+ #[derive(Default, Debug, Deserialize)]
1190
+ #[serde(default, rename_all = "PascalCase")]
1191
+ struct OssError {
1192
+ code: String,
1193
+ message: String,
1194
+ request_id: String,
1195
+ host_id: String,
1196
+ }
1197
+
1198
+ /// Context needed to classify an error from this service.
1199
+ #[derive(Clone, Copy, Debug)]
1200
+ pub(crate) struct ErrorContext {
1201
+ service_operation: ServiceOperation,
1202
+ caller_condition: bool,
1203
+ if_not_exists: bool,
1204
+ }
1205
+
1206
+ impl ErrorContext {
1207
+ pub(crate) const fn new(service_operation: ServiceOperation) -> Self {
1208
+ Self {
1209
+ service_operation,
1210
+ caller_condition: false,
1211
+ if_not_exists: false,
1229
1212
  }
1230
1213
  }
1214
+
1215
+ pub(crate) const fn with_caller_condition(mut self, caller_condition: bool) -> Self {
1216
+ self.caller_condition = caller_condition;
1217
+ self
1218
+ }
1219
+
1220
+ pub(crate) const fn with_if_not_exists(mut self, if_not_exists: bool) -> Self {
1221
+ self.caller_condition = self.caller_condition || if_not_exists;
1222
+ self.if_not_exists = if_not_exists;
1223
+ self
1224
+ }
1225
+ }
1226
+
1227
+ /// Parse an error response using its service request context.
1228
+ pub(crate) fn parse_error(ctx: ErrorContext, resp: Response<Buffer>) -> Error {
1229
+ let (parts, body) = resp.into_parts();
1230
+ let bs = body.to_bytes();
1231
+
1232
+ let oss_error = de::from_reader::<_, OssError>(bs.clone().reader()).ok();
1233
+
1234
+ let (mut kind, mut retryable) = match parts.status {
1235
+ StatusCode::NOT_FOUND => (ErrorKind::NotFound, false),
1236
+ StatusCode::FORBIDDEN => (ErrorKind::PermissionDenied, false),
1237
+ StatusCode::PRECONDITION_FAILED | StatusCode::NOT_MODIFIED if ctx.caller_condition => {
1238
+ (ErrorKind::ConditionNotMatch, false)
1239
+ }
1240
+ StatusCode::TOO_MANY_REQUESTS => (ErrorKind::RateLimited, true),
1241
+ StatusCode::INTERNAL_SERVER_ERROR
1242
+ | StatusCode::BAD_GATEWAY
1243
+ | StatusCode::SERVICE_UNAVAILABLE
1244
+ | StatusCode::GATEWAY_TIMEOUT => (ErrorKind::Unexpected, true),
1245
+ _ => (ErrorKind::Unexpected, false),
1246
+ };
1247
+
1248
+ if let Some(oss_error) = &oss_error {
1249
+ match oss_error.code.as_str() {
1250
+ "PreconditionFailed" if ctx.caller_condition => {
1251
+ (kind, retryable) = (ErrorKind::ConditionNotMatch, false);
1252
+ }
1253
+ "FileAlreadyExists" if ctx.if_not_exists => {
1254
+ (kind, retryable) = (ErrorKind::ConditionNotMatch, false);
1255
+ }
1256
+ "FileAlreadyExists" | "FileImmutable" => {
1257
+ (kind, retryable) = (ErrorKind::Conflict, false);
1258
+ }
1259
+ _ if matches!(
1260
+ parts.status,
1261
+ StatusCode::CONFLICT | StatusCode::PRECONDITION_FAILED
1262
+ ) =>
1263
+ {
1264
+ (kind, retryable) = (ErrorKind::Unexpected, false);
1265
+ }
1266
+ _ => {}
1267
+ }
1268
+ }
1269
+
1270
+ let message = match oss_error {
1271
+ Some(oss_err) => format!("{oss_err:?}"),
1272
+ None => String::from_utf8_lossy(&bs).into_owned(),
1273
+ };
1274
+
1275
+ let mut err = Error::new(kind, message);
1276
+
1277
+ err = err.with_context("service_operation", ctx.service_operation.0);
1278
+ err = with_error_response_context(err, parts);
1279
+
1280
+ if retryable {
1281
+ err = err.set_temporary();
1282
+ }
1283
+
1284
+ err
1231
1285
  }
1232
1286
 
1233
- pub(super) use error::*;
1287
+ #[cfg(test)]
1288
+ mod error_tests {
1289
+ use super::*;
1290
+
1291
+ fn parse_oss_error(ctx: ErrorContext, status: StatusCode, code: &str) -> Error {
1292
+ let body = Buffer::from(format!(
1293
+ "<Error><Code>{code}</Code><Message>test</Message></Error>"
1294
+ ));
1295
+ let resp = Response::builder()
1296
+ .status(status)
1297
+ .body(body)
1298
+ .expect("response must build");
1299
+ parse_error(ctx, resp)
1300
+ }
1301
+
1302
+ #[test]
1303
+ fn conflict_classification_uses_native_code_and_condition() {
1304
+ let conditional = ErrorContext::new(ServiceOperation("PutObject")).with_if_not_exists(true);
1305
+ assert_eq!(
1306
+ parse_oss_error(conditional, StatusCode::CONFLICT, "FileAlreadyExists").kind(),
1307
+ ErrorKind::ConditionNotMatch
1308
+ );
1309
+ assert_eq!(
1310
+ parse_oss_error(
1311
+ conditional,
1312
+ StatusCode::PRECONDITION_FAILED,
1313
+ "PreconditionFailed"
1314
+ )
1315
+ .kind(),
1316
+ ErrorKind::ConditionNotMatch
1317
+ );
1318
+
1319
+ let unconditional = ErrorContext::new(ServiceOperation("PutObject"));
1320
+ assert_eq!(
1321
+ parse_oss_error(unconditional, StatusCode::CONFLICT, "FileAlreadyExists").kind(),
1322
+ ErrorKind::Conflict
1323
+ );
1324
+ assert_eq!(
1325
+ parse_oss_error(unconditional, StatusCode::CONFLICT, "FileImmutable").kind(),
1326
+ ErrorKind::Conflict
1327
+ );
1328
+ assert_eq!(
1329
+ parse_oss_error(unconditional, StatusCode::CONFLICT, "UnknownConflict").kind(),
1330
+ ErrorKind::Unexpected
1331
+ );
1332
+ }
1333
+ }
@@ -46,7 +46,10 @@ impl oio::BatchDelete for OssDeleter {
46
46
 
47
47
  match status {
48
48
  StatusCode::NO_CONTENT | StatusCode::NOT_FOUND => Ok(()),
49
- _ => Err(parse_error(resp)),
49
+ _ => Err(parse_error(
50
+ ErrorContext::new(ServiceOperation("DeleteObject")),
51
+ resp,
52
+ )),
50
53
  }
51
54
  }
52
55
 
@@ -63,7 +66,10 @@ impl oio::BatchDelete for OssDeleter {
63
66
  let status = resp.status();
64
67
 
65
68
  if status != StatusCode::OK {
66
- return Err(parse_error(resp));
69
+ return Err(parse_error(
70
+ ErrorContext::new(ServiceOperation("DeleteMultipleObjects")),
71
+ resp,
72
+ ));
67
73
  }
68
74
 
69
75
  let bs = resp.into_body();
@@ -88,10 +94,14 @@ impl oio::BatchDelete for OssDeleter {
88
94
 
89
95
  for i in result.deleted {
90
96
  let path = build_rel_path(&self.core.root, &i.key);
91
- let mut op = OpDelete::default();
92
- if let Some(version) = &i.version_id {
93
- op = op.with_version(version);
94
- }
97
+ let op = OpDelete::from_options(
98
+ &Capability::default(),
99
+ options::DeleteOptions {
100
+ version: i.version_id,
101
+ ..Default::default()
102
+ },
103
+ )
104
+ .expect("delete result does not contain a logical condition");
95
105
  let object = (path, op);
96
106
  keys.remove(&object);
97
107
  batched_result.succeeded.push(object);
@@ -80,7 +80,10 @@ impl oio::PageList for OssLister {
80
80
  .await?;
81
81
 
82
82
  if resp.status() != http::StatusCode::OK {
83
- return Err(parse_error(resp));
83
+ return Err(parse_error(
84
+ ErrorContext::new(ServiceOperation("ListObjectsV2")),
85
+ resp,
86
+ ));
84
87
  }
85
88
 
86
89
  let bs = resp.into_body();
@@ -94,7 +97,7 @@ impl oio::PageList for OssLister {
94
97
  for prefix in output.common_prefixes {
95
98
  let de = oio::Entry::new(
96
99
  &build_rel_path(&self.core.root, &prefix.prefix),
97
- Metadata::new(EntryMode::DIR),
100
+ MetadataBuilder::dir().build(),
98
101
  );
99
102
  ctx.entries.push_back(de);
100
103
  }
@@ -108,14 +111,17 @@ impl oio::PageList for OssLister {
108
111
  continue;
109
112
  }
110
113
 
111
- let mut meta = Metadata::new(EntryMode::from_path(&path));
112
- meta.set_is_current(true);
113
- meta.set_etag(&object.etag);
114
- meta.set_content_md5(object.etag.trim_matches('"'));
115
- meta.set_content_length(object.size);
116
- meta.set_last_modified(object.last_modified.parse::<Timestamp>()?);
117
-
118
- let de = oio::Entry::with(path, meta);
114
+ let mut meta = if path.ends_with('/') {
115
+ MetadataBuilder::dir()
116
+ } else {
117
+ MetadataBuilder::file(object.size)
118
+ };
119
+ meta.is_current(Some(true));
120
+ meta.etag(&object.etag);
121
+ meta.content_md5(object.etag.trim_matches('"'));
122
+ meta.last_modified(object.last_modified.parse::<Timestamp>()?);
123
+
124
+ let de = oio::Entry::with(path, meta.build());
119
125
  ctx.entries.push_back(de);
120
126
  }
121
127
 
@@ -176,7 +182,10 @@ impl oio::PageList for OssObjectVersionsLister {
176
182
  )
177
183
  .await?;
178
184
  if resp.status() != http::StatusCode::OK {
179
- return Err(parse_error(resp));
185
+ return Err(parse_error(
186
+ ErrorContext::new(ServiceOperation("ListObjectVersions")),
187
+ resp,
188
+ ));
180
189
  }
181
190
 
182
191
  let body = resp.into_body();
@@ -203,7 +212,7 @@ impl oio::PageList for OssObjectVersionsLister {
203
212
  for prefix in output.common_prefixes {
204
213
  let de = oio::Entry::new(
205
214
  &build_rel_path(&self.core.root, &prefix.prefix),
206
- Metadata::new(EntryMode::DIR),
215
+ MetadataBuilder::dir().build(),
207
216
  );
208
217
  ctx.entries.push_back(de);
209
218
  }
@@ -222,17 +231,20 @@ impl oio::PageList for OssObjectVersionsLister {
222
231
  path = "/".to_owned();
223
232
  }
224
233
 
225
- let mut meta = Metadata::new(EntryMode::from_path(&path));
226
- meta.set_version(&version_object.version_id);
227
- meta.set_is_current(version_object.is_latest);
228
- meta.set_content_length(version_object.size);
229
- meta.set_last_modified(version_object.last_modified.parse::<Timestamp>()?);
234
+ let mut meta = if path.ends_with('/') {
235
+ MetadataBuilder::dir()
236
+ } else {
237
+ MetadataBuilder::file(version_object.size)
238
+ };
239
+ meta.version(&version_object.version_id);
240
+ meta.is_current(Some(version_object.is_latest));
241
+ meta.last_modified(version_object.last_modified.parse::<Timestamp>()?);
230
242
  if let Some(etag) = version_object.etag {
231
- meta.set_etag(&etag);
232
- meta.set_content_md5(etag.trim_matches('"'));
243
+ meta.etag(&etag);
244
+ meta.content_md5(etag.trim_matches('"'));
233
245
  }
234
246
 
235
- let entry = oio::Entry::new(&path, meta);
247
+ let entry = oio::Entry::new(&path, meta.build());
236
248
  ctx.entries.push_back(entry);
237
249
  }
238
250
 
@@ -243,13 +255,17 @@ impl oio::PageList for OssObjectVersionsLister {
243
255
  path = "/".to_owned();
244
256
  }
245
257
 
246
- let mut meta = Metadata::new(EntryMode::FILE);
247
- meta.set_version(&delete_marker.version_id);
248
- meta.set_is_deleted(true);
249
- meta.set_is_current(delete_marker.is_latest);
250
- meta.set_last_modified(delete_marker.last_modified.parse::<Timestamp>()?);
251
-
252
- let entry = oio::Entry::new(&path, meta);
258
+ let mut meta = if path.ends_with('/') {
259
+ MetadataBuilder::dir()
260
+ } else {
261
+ MetadataBuilder::file(0)
262
+ };
263
+ meta.version(&delete_marker.version_id);
264
+ meta.is_deleted(true);
265
+ meta.is_current(Some(delete_marker.is_latest));
266
+ meta.last_modified(delete_marker.last_modified.parse::<Timestamp>()?);
267
+
268
+ let entry = oio::Entry::new(&path, meta.build());
253
269
  ctx.entries.push_back(entry);
254
270
  }
255
271
  }
@@ -16,7 +16,7 @@
16
16
  // under the License.
17
17
 
18
18
  use super::backend::*;
19
- use super::core::parse_error;
19
+ use super::core::{ErrorContext, parse_error};
20
20
  use http::Response;
21
21
  use http::StatusCode;
22
22
  use opendal_core::raw::*;
@@ -66,7 +66,11 @@ impl oio::StreamRead for OssReader {
66
66
  _ => {
67
67
  let (part, mut body) = resp.into_parts();
68
68
  let buf = body.to_buffer().await?;
69
- return Err(parse_error(Response::from_parts(part, buf)));
69
+ return Err(parse_error(
70
+ ErrorContext::new(ServiceOperation("GetObject"))
71
+ .with_caller_condition(args.is_conditional()),
72
+ Response::from_parts(part, buf),
73
+ ));
70
74
  }
71
75
  };
72
76
 
@@ -47,18 +47,18 @@ impl OssWriter {
47
47
  }
48
48
 
49
49
  fn parse_metadata(headers: &HeaderMap<HeaderValue>) -> Result<Metadata> {
50
- let mut meta = Metadata::default();
50
+ let mut meta = MetadataBuilder::unknown();
51
51
  if let Some(etag) = parse_etag(headers)? {
52
- meta.set_etag(etag);
52
+ meta.etag(etag);
53
53
  }
54
54
  if let Some(md5) = parse_content_md5(headers)? {
55
- meta.set_content_md5(md5);
55
+ meta.content_md5(md5);
56
56
  }
57
57
  if let Some(version) = parse_header_to_str(headers, constants::X_OSS_VERSION_ID)? {
58
- meta.set_version(version);
58
+ meta.version(version);
59
59
  }
60
60
 
61
- Ok(meta)
61
+ Ok(meta.build())
62
62
  }
63
63
  }
64
64
 
@@ -76,22 +76,18 @@ impl oio::MultipartWrite for OssWriter {
76
76
 
77
77
  match status {
78
78
  StatusCode::CREATED | StatusCode::OK => Ok(meta),
79
- _ => Err(parse_error(resp)),
79
+ _ => Err(parse_error(
80
+ ErrorContext::new(ServiceOperation("PutObject"))
81
+ .with_if_not_exists(self.op.if_not_exists()),
82
+ resp,
83
+ )),
80
84
  }
81
85
  }
82
86
 
83
87
  async fn initiate_part(&self) -> Result<String> {
84
88
  let resp = self
85
89
  .core
86
- .oss_initiate_upload(
87
- &self.ctx,
88
- &self.path,
89
- self.op.content_type(),
90
- self.op.content_disposition(),
91
- self.op.cache_control(),
92
- self.op.content_encoding(),
93
- false,
94
- )
90
+ .oss_initiate_upload(&self.ctx, &self.path, &self.op, false)
95
91
  .await?;
96
92
 
97
93
  let status = resp.status();
@@ -106,7 +102,11 @@ impl oio::MultipartWrite for OssWriter {
106
102
 
107
103
  Ok(result.upload_id)
108
104
  }
109
- _ => Err(parse_error(resp)),
105
+ _ => Err(parse_error(
106
+ ErrorContext::new(ServiceOperation("InitiateMultipartUpload"))
107
+ .with_if_not_exists(self.op.if_not_exists()),
108
+ resp,
109
+ )),
110
110
  }
111
111
  }
112
112
 
@@ -153,7 +153,10 @@ impl oio::MultipartWrite for OssWriter {
153
153
  size: None,
154
154
  })
155
155
  }
156
- _ => Err(parse_error(resp)),
156
+ _ => Err(parse_error(
157
+ ErrorContext::new(ServiceOperation("UploadPart")),
158
+ resp,
159
+ )),
157
160
  }
158
161
  }
159
162
 
@@ -172,7 +175,9 @@ impl oio::MultipartWrite for OssWriter {
172
175
 
173
176
  let resp = self
174
177
  .core
175
- .oss_complete_multipart_upload_request(&self.ctx, &self.path, upload_id, false, parts)
178
+ .oss_complete_multipart_upload_request(
179
+ &self.ctx, &self.path, upload_id, false, parts, &self.op,
180
+ )
176
181
  .await?;
177
182
 
178
183
  let meta = Self::parse_metadata(resp.headers())?;
@@ -180,7 +185,11 @@ impl oio::MultipartWrite for OssWriter {
180
185
 
181
186
  match status {
182
187
  StatusCode::OK => Ok(meta),
183
- _ => Err(parse_error(resp)),
188
+ _ => Err(parse_error(
189
+ ErrorContext::new(ServiceOperation("CompleteMultipartUpload"))
190
+ .with_if_not_exists(self.op.if_not_exists()),
191
+ resp,
192
+ )),
184
193
  }
185
194
  }
186
195
 
@@ -192,7 +201,10 @@ impl oio::MultipartWrite for OssWriter {
192
201
  match resp.status() {
193
202
  // OSS returns code 204 if abort succeeds.
194
203
  StatusCode::NO_CONTENT => Ok(()),
195
- _ => Err(parse_error(resp)),
204
+ _ => Err(parse_error(
205
+ ErrorContext::new(ServiceOperation("AbortMultipartUpload")),
206
+ resp,
207
+ )),
196
208
  }
197
209
  }
198
210
  }
@@ -216,7 +228,10 @@ impl oio::AppendWrite for OssWriter {
216
228
  Ok(content_length)
217
229
  }
218
230
  StatusCode::NOT_FOUND => Ok(0),
219
- _ => Err(parse_error(resp)),
231
+ _ => Err(parse_error(
232
+ ErrorContext::new(ServiceOperation("HeadObject")),
233
+ resp,
234
+ )),
220
235
  }
221
236
  }
222
237
 
@@ -233,7 +248,10 @@ impl oio::AppendWrite for OssWriter {
233
248
 
234
249
  match status {
235
250
  StatusCode::OK => Ok(meta),
236
- _ => Err(parse_error(resp)),
251
+ _ => Err(parse_error(
252
+ ErrorContext::new(ServiceOperation("AppendObject")),
253
+ resp,
254
+ )),
237
255
  }
238
256
  }
239
257
  }
@@ -35,7 +35,7 @@ all-features = true
35
35
  bytes = { workspace = true }
36
36
  http = { workspace = true }
37
37
  log = { workspace = true }
38
- opendal-core = { path = "../../core", version = "0.58.1", default-features = false }
38
+ opendal-core = { path = "../../core", version = "0.59.0", default-features = false }
39
39
  serde = { workspace = true, features = ["derive"] }
40
40
  serde_json = { workspace = true }
41
41