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
@@ -19,13 +19,13 @@ use std::fmt::Debug;
19
19
  use std::sync::Arc;
20
20
  use std::time::Duration;
21
21
 
22
+ use asyncband::mutex::Mutex;
22
23
  use bytes::Buf;
23
24
  use bytes::Bytes;
24
25
  use http::Request;
25
26
  use http::Response;
26
27
  use http::StatusCode;
27
28
  use http::header;
28
- use mea::mutex::Mutex;
29
29
 
30
30
  use opendal_core::raw::*;
31
31
  use opendal_core::*;
@@ -132,10 +132,20 @@ impl OneDriveCore {
132
132
  serde_json::from_reader(bytes.reader())
133
133
  .map_err(new_json_deserialize_error)?
134
134
  }
135
- _ => return Err(parse_error(response)),
135
+ _ => {
136
+ return Err(parse_error(
137
+ ErrorContext::new(ServiceOperation("GetItem")),
138
+ response,
139
+ ));
140
+ }
136
141
  }
137
142
  }
138
- _ => return Err(parse_error(response)),
143
+ _ => {
144
+ return Err(parse_error(
145
+ ErrorContext::new(ServiceOperation("GetItem")),
146
+ response,
147
+ ));
148
+ }
139
149
  };
140
150
 
141
151
  Ok(item)
@@ -164,24 +174,14 @@ const MONITOR_WAIT_SECOND: u64 = 1;
164
174
  // `services-onedrive` uses the file path based API for simplicity.
165
175
  // Read more at https://learn.microsoft.com/en-us/graph/onedrive-addressing-driveitems
166
176
  impl OneDriveCore {
167
- /// Send a stat request about a particular path, including:
168
- ///
169
- /// - Get stat object only if ETag not matches
170
- /// - whether to get the object version
171
- ///
172
- /// See also [`onedrive_get_stat_plain()`].
177
+ /// Send a stat request about a particular path.
173
178
  pub(crate) async fn onedrive_stat(
174
179
  &self,
175
180
  ctx: &OperationContext,
176
181
  path: &str,
177
182
  args: OpStat,
178
183
  ) -> Result<Metadata> {
179
- let mut url: String = self.onedrive_item_url(path, true);
180
- if args.version().is_some() {
181
- url += "?$expand=versions(";
182
- url += VERSION_SELECT_PARAM;
183
- url += ")";
184
- }
184
+ let url: String = self.onedrive_item_url(path, true);
185
185
 
186
186
  let mut request = Request::get(&url);
187
187
  if let Some(etag) = args.if_none_match() {
@@ -198,7 +198,11 @@ impl OneDriveCore {
198
198
 
199
199
  let response = ctx.http_transport().send(request).await?;
200
200
  if !response.status().is_success() {
201
- return Err(parse_error(response));
201
+ return Err(parse_error(
202
+ ErrorContext::new(ServiceOperation("GetItem"))
203
+ .with_caller_condition(args.is_conditional()),
204
+ response,
205
+ ));
202
206
  }
203
207
 
204
208
  let bytes = response.into_body();
@@ -210,31 +214,18 @@ impl OneDriveCore {
210
214
  ItemType::File { .. } => EntryMode::FILE,
211
215
  };
212
216
 
213
- let mut meta = Metadata::new(entry_mode)
214
- .with_etag(decoded_response.e_tag)
215
- .with_content_length(decoded_response.size.max(0) as u64);
216
-
217
- if let Some(version) = args.version() {
218
- for item_version in decoded_response.versions.as_deref().unwrap_or_default() {
219
- if item_version.id == version {
220
- meta.set_version(version);
221
- break; // early exit
222
- }
223
- }
224
-
225
- if meta.version().is_none() {
226
- return Err(Error::new(
227
- ErrorKind::NotFound,
228
- "cannot find this version of the item",
229
- ));
230
- }
231
- }
217
+ let mut meta = if entry_mode == EntryMode::FILE {
218
+ MetadataBuilder::file(decoded_response.size.max(0) as u64)
219
+ } else {
220
+ MetadataBuilder::dir()
221
+ };
222
+ meta.etag(decoded_response.e_tag);
232
223
 
233
224
  let last_modified = decoded_response.last_modified_date_time;
234
225
  let date_utc_last_modified = last_modified.parse::<Timestamp>()?;
235
- meta.set_last_modified(date_utc_last_modified);
226
+ meta.last_modified(date_utc_last_modified);
236
227
 
237
- Ok(meta)
228
+ Ok(meta.build())
238
229
  }
239
230
 
240
231
  /// Return versions of an item
@@ -263,6 +254,12 @@ impl OneDriveCore {
263
254
  self.sign(ctx, &mut request).await?;
264
255
 
265
256
  let response = ctx.http_transport().send(request).await?;
257
+ if !response.status().is_success() {
258
+ return Err(parse_error(
259
+ ErrorContext::new(ServiceOperation("ListVersions")),
260
+ response,
261
+ ));
262
+ }
266
263
  let decoded_response: GraphApiOneDriveVersionsResponse =
267
264
  serde_json::from_reader(response.into_body().reader())
268
265
  .map_err(new_json_deserialize_error)?;
@@ -453,11 +450,10 @@ impl OneDriveCore {
453
450
  path: &str,
454
451
  args: &OpWrite,
455
452
  ) -> Result<Response<Buffer>> {
456
- let parent_path = get_parent(path);
457
453
  let file_name = get_basename(path);
458
454
  let url = format!(
459
455
  "{}:/createUploadSession",
460
- self.onedrive_item_url(parent_path, true),
456
+ self.onedrive_item_url(path, true),
461
457
  );
462
458
  let mut request = Request::post(url).header(header::CONTENT_TYPE, "application/json");
463
459
 
@@ -552,7 +548,10 @@ impl OneDriveCore {
552
548
  // we must validate if source exist
553
549
  let response = self.onedrive_get_stat_plain(ctx, source).await?;
554
550
  if !response.status().is_success() {
555
- return Err(parse_error(response));
551
+ return Err(parse_error(
552
+ ErrorContext::new(ServiceOperation("GetItem")),
553
+ response,
554
+ ));
556
555
  }
557
556
 
558
557
  // We need to stat the destination parent folder to get a parent reference
@@ -580,11 +579,21 @@ impl OneDriveCore {
580
579
  let response = self.onedrive_delete(ctx, destination).await?;
581
580
  match response.status() {
582
581
  StatusCode::NO_CONTENT | StatusCode::NOT_FOUND => {} // expected, intentionally empty
583
- _ => return Err(parse_error(response)),
582
+ _ => {
583
+ return Err(parse_error(
584
+ ErrorContext::new(ServiceOperation("DeleteItem")),
585
+ response,
586
+ ));
587
+ }
584
588
  }
585
589
  }
586
590
  StatusCode::NOT_FOUND => {} // expected, intentionally empty
587
- _ => return Err(parse_error(response)),
591
+ _ => {
592
+ return Err(parse_error(
593
+ ErrorContext::new(ServiceOperation("GetItem")),
594
+ response,
595
+ ));
596
+ }
588
597
  }
589
598
 
590
599
  let url: String = format!("{}:/copy", self.onedrive_item_url(source, true));
@@ -610,7 +619,10 @@ impl OneDriveCore {
610
619
  )
611
620
  })
612
621
  .map(String::from),
613
- _ => Err(parse_error(response)),
622
+ _ => Err(parse_error(
623
+ ErrorContext::new(ServiceOperation("CopyItem")),
624
+ response,
625
+ )),
614
626
  }
615
627
  }
616
628
 
@@ -692,7 +704,10 @@ impl OneDriveCore {
692
704
  match response.status() {
693
705
  // can get etag, metadata, etc...
694
706
  StatusCode::OK => Ok(()),
695
- _ => Err(parse_error(response)),
707
+ _ => Err(parse_error(
708
+ ErrorContext::new(ServiceOperation("MoveItem")),
709
+ response,
710
+ )),
696
711
  }
697
712
  }
698
713
  }
@@ -753,7 +768,10 @@ impl OneDriveSigner {
753
768
  - Duration::from_secs(120); // assumes 2 mins graceful transmission for implementation simplicity
754
769
  Ok(())
755
770
  }
756
- _ => Err(parse_error(response)),
771
+ _ => Err(parse_error(
772
+ ErrorContext::new(ServiceOperation("RefreshAccessToken")),
773
+ response,
774
+ )),
757
775
  }
758
776
  }
759
777
 
@@ -896,13 +914,7 @@ mod tests {
896
914
  async fn list_root_returns_entries() {
897
915
  let core = test_core("/");
898
916
  let ctx = test_ctx();
899
- let lister = OneDriveLister::new(
900
- "/".to_string(),
901
- core,
902
- ctx,
903
- Capability::default(),
904
- &OpList::default(),
905
- );
917
+ let lister = OneDriveLister::new("/".to_string(), core, ctx, &OpList::default());
906
918
  let mut lister = oio::PageLister::new(lister);
907
919
 
908
920
  let mut entries = Vec::new();
@@ -915,53 +927,142 @@ mod tests {
915
927
  assert_eq!(entries[1].path(), "test.txt");
916
928
  assert_eq!(entries[1].mode(), EntryMode::FILE);
917
929
  }
918
- }
919
930
 
920
- mod error {
921
- use http::Response;
922
- use http::StatusCode;
931
+ #[test]
932
+ fn invalid_request_is_temporary() {
933
+ let response = Response::builder()
934
+ .status(StatusCode::BAD_REQUEST)
935
+ .body(Buffer::from(r#"{"error":{"code":"invalidRequest"}}"#))
936
+ .unwrap();
937
+
938
+ let err = parse_error(
939
+ ErrorContext::new(ServiceOperation("CreateFolder")),
940
+ response,
941
+ );
923
942
 
924
- use opendal_core::raw::*;
925
- use opendal_core::*;
926
-
927
- /// Parse error response into Error.
928
- pub(crate) fn parse_error(response: Response<Buffer>) -> Error {
929
- let (parts, body) = response.into_parts();
930
- let bs = body.to_bytes();
931
-
932
- let (kind, retryable) = match parts.status {
933
- StatusCode::NOT_FOUND => (ErrorKind::NotFound, false),
934
- // The OneDrive service replaces resources.
935
- // However, the Onedrive doesn't have Strong Read-After-Write properties,
936
- // the concurrent requests to create directories might result in errors.
937
- //
938
- // Running behavior tests can yield HTTP 409 Conflict because of the consistency guarantee.
939
- //
940
- // Read more about `REPLACE_EXISTING_ITEM_WHEN_CONFLICT` in `graph_model.rs`.
941
- StatusCode::CONFLICT => (ErrorKind::AlreadyExists, true),
942
- StatusCode::FORBIDDEN => (ErrorKind::PermissionDenied, false),
943
- StatusCode::INTERNAL_SERVER_ERROR
944
- | StatusCode::BAD_GATEWAY
945
- | StatusCode::SERVICE_UNAVAILABLE
946
- | StatusCode::GATEWAY_TIMEOUT => (ErrorKind::Unexpected, true),
947
- StatusCode::NOT_MODIFIED | StatusCode::PRECONDITION_FAILED => {
948
- (ErrorKind::ConditionNotMatch, false)
949
- }
950
- _ => (ErrorKind::Unexpected, false),
943
+ assert_eq!(err.kind(), ErrorKind::Unexpected);
944
+ assert!(err.is_temporary());
945
+ }
946
+
947
+ #[test]
948
+ fn conflict_classification_uses_native_code() {
949
+ let parse_conflict = |code: &str, operation| {
950
+ let body = Buffer::from(format!(r#"{{"error":{{"code":"{code}"}}}}"#));
951
+ let response = Response::builder()
952
+ .status(StatusCode::CONFLICT)
953
+ .body(body)
954
+ .expect("response must build");
955
+ parse_error(ErrorContext::new(operation), response)
951
956
  };
952
957
 
953
- let message = String::from_utf8_lossy(&bs);
958
+ let concurrent = parse_conflict(
959
+ "Directory_ConcurrencyViolation",
960
+ ServiceOperation("CreateFolder"),
961
+ );
962
+ assert_eq!(concurrent.kind(), ErrorKind::Conflict);
963
+ assert!(concurrent.is_temporary());
964
+
965
+ let name_conflict = parse_conflict("nameAlreadyExists", ServiceOperation("UploadFragment"));
966
+ assert_eq!(name_conflict.kind(), ErrorKind::Conflict);
967
+ assert!(!name_conflict.is_temporary());
968
+
969
+ assert_eq!(
970
+ parse_conflict("unknownConflict", ServiceOperation("MoveItem")).kind(),
971
+ ErrorKind::Unexpected
972
+ );
973
+ }
954
974
 
955
- let mut err = Error::new(kind, message);
975
+ #[test]
976
+ fn precondition_failed_requires_caller_condition() {
977
+ let response = || {
978
+ Response::builder()
979
+ .status(StatusCode::PRECONDITION_FAILED)
980
+ .body(Buffer::new())
981
+ .expect("response must build")
982
+ };
983
+ let caller =
984
+ ErrorContext::new(ServiceOperation("UploadContent")).with_caller_condition(true);
985
+ assert_eq!(
986
+ parse_error(caller, response()).kind(),
987
+ ErrorKind::ConditionNotMatch
988
+ );
989
+ let no_condition = ErrorContext::new(ServiceOperation("UploadFragment"));
990
+ assert_eq!(
991
+ parse_error(no_condition, response()).kind(),
992
+ ErrorKind::Unexpected
993
+ );
994
+ }
995
+ }
956
996
 
957
- err = with_error_response_context(err, parts);
997
+ use crate::graph_model::GraphErrorResponse;
958
998
 
959
- if retryable {
960
- err = err.set_temporary();
999
+ /// Context needed to classify an error from this service.
1000
+ #[derive(Clone, Copy, Debug)]
1001
+ pub(crate) struct ErrorContext {
1002
+ service_operation: ServiceOperation,
1003
+ caller_condition: bool,
1004
+ }
1005
+
1006
+ impl ErrorContext {
1007
+ pub(crate) const fn new(service_operation: ServiceOperation) -> Self {
1008
+ Self {
1009
+ service_operation,
1010
+ caller_condition: false,
961
1011
  }
1012
+ }
962
1013
 
963
- err
1014
+ pub(crate) const fn with_caller_condition(mut self, caller_condition: bool) -> Self {
1015
+ self.caller_condition = caller_condition;
1016
+ self
964
1017
  }
965
1018
  }
966
1019
 
967
- pub(super) use error::*;
1020
+ /// Parse an error response using its service request context.
1021
+ pub(crate) fn parse_error(ctx: ErrorContext, response: Response<Buffer>) -> Error {
1022
+ let (parts, body) = response.into_parts();
1023
+ let bs = body.to_bytes();
1024
+
1025
+ let graph_error = serde_json::from_slice::<GraphErrorResponse>(&bs).ok();
1026
+ let graph_code = graph_error
1027
+ .as_ref()
1028
+ .map(|response| response.error.code.as_str());
1029
+ let consistency_lag =
1030
+ parts.status == StatusCode::BAD_REQUEST && graph_code == Some("invalidRequest");
1031
+
1032
+ let (kind, mut retryable) = match parts.status {
1033
+ StatusCode::NOT_FOUND => (ErrorKind::NotFound, false),
1034
+ StatusCode::CONFLICT if graph_code == Some("Directory_ConcurrencyViolation") => {
1035
+ (ErrorKind::Conflict, true)
1036
+ }
1037
+ StatusCode::CONFLICT if graph_code == Some("nameAlreadyExists") => {
1038
+ (ErrorKind::Conflict, false)
1039
+ }
1040
+ StatusCode::CONFLICT => (ErrorKind::Unexpected, false),
1041
+ StatusCode::FORBIDDEN => (ErrorKind::PermissionDenied, false),
1042
+ StatusCode::INTERNAL_SERVER_ERROR
1043
+ | StatusCode::BAD_GATEWAY
1044
+ | StatusCode::SERVICE_UNAVAILABLE
1045
+ | StatusCode::GATEWAY_TIMEOUT => (ErrorKind::Unexpected, true),
1046
+ StatusCode::NOT_MODIFIED | StatusCode::PRECONDITION_FAILED if ctx.caller_condition => {
1047
+ (ErrorKind::ConditionNotMatch, false)
1048
+ }
1049
+ _ => (ErrorKind::Unexpected, false),
1050
+ };
1051
+
1052
+ if consistency_lag {
1053
+ retryable = true;
1054
+ }
1055
+
1056
+ let message = String::from_utf8_lossy(&bs);
1057
+
1058
+ let mut err = Error::new(kind, message);
1059
+
1060
+ err = err.with_context("service_operation", ctx.service_operation.0);
1061
+ err = with_error_response_context(err, parts);
1062
+
1063
+ if retryable {
1064
+ err = err.set_temporary();
1065
+ }
1066
+
1067
+ err
1068
+ }
@@ -22,8 +22,8 @@ use http::StatusCode;
22
22
  use opendal_core::raw::*;
23
23
  use opendal_core::*;
24
24
 
25
- use super::core::OneDriveCore;
26
25
  use super::core::parse_error;
26
+ use super::core::{ErrorContext, OneDriveCore};
27
27
 
28
28
  /// Delete operation
29
29
  /// Documentation: https://learn.microsoft.com/en-us/onedrive/developer/rest-api/api/driveitem_delete?view=odsp-graph-online
@@ -43,7 +43,10 @@ impl oio::OneShotDelete for OneDriveDeleter {
43
43
  let response = self.core.onedrive_delete(&self.ctx, &path).await?;
44
44
  match response.status() {
45
45
  StatusCode::NO_CONTENT | StatusCode::NOT_FOUND => Ok(()),
46
- _ => Err(parse_error(response)),
46
+ _ => Err(parse_error(
47
+ ErrorContext::new(ServiceOperation("DeleteItem")),
48
+ response,
49
+ )),
47
50
  }
48
51
  }
49
52
  }
@@ -18,6 +18,16 @@
18
18
  use serde::Deserialize;
19
19
  use serde::Serialize;
20
20
 
21
+ #[derive(Debug, Deserialize)]
22
+ pub struct GraphErrorResponse {
23
+ pub error: GraphError,
24
+ }
25
+
26
+ #[derive(Debug, Deserialize)]
27
+ pub struct GraphError {
28
+ pub code: String,
29
+ }
30
+
21
31
  #[derive(Debug, Deserialize)]
22
32
  pub struct GraphOAuthRefreshTokenResponseBody {
23
33
  pub access_token: String,
@@ -56,7 +66,6 @@ pub struct OneDriveItem {
56
66
  pub parent_reference: ParentReference,
57
67
  #[serde(flatten)]
58
68
  pub item_type: ItemType,
59
- pub versions: Option<Vec<OneDriveItemVersion>>,
60
69
  }
61
70
 
62
71
  #[derive(Debug, Clone, Serialize, Deserialize)]
@@ -327,7 +336,6 @@ mod tests {
327
336
  assert_eq!(item.e_tag, "\"{3B131E1C-7D81-20AF-80D0-720000000000},2\"");
328
337
  assert_eq!(item.size, 3);
329
338
  assert_eq!(item.parent_reference.id, "A0AA0A000A000A0A!113");
330
- assert!(item.versions.is_none());
331
339
  if let ItemType::File { file, .. } = &item.item_type {
332
340
  assert_eq!(file.mime_type, "text/plain");
333
341
  } else {
@@ -335,55 +343,6 @@ mod tests {
335
343
  }
336
344
  }
337
345
 
338
- #[test]
339
- fn test_parse_one_drive_file_with_version_json() {
340
- let data = r#"{
341
- "@odata.context": "https://graph.microsoft.com/v1.0/$metadata#users('opendal%40outlook.com')/drive/root(id,name,lastModifiedDateTime,eTag,size,parentReference,folder,file)/$entity",
342
- "@odata.etag": "\"{3B131E1C-7D81-20AF-80D0-720000000000},2\"",
343
- "eTag": "\"{3B131E1C-7D81-20AF-80D0-720000000000},2\"",
344
- "id": "A0AA0A000A000A0A!114",
345
- "lastModifiedDateTime": "2025-02-16T19:49:05Z",
346
- "name": "filename.txt",
347
- "size": 3,
348
- "parentReference": {
349
- "driveType": "personal",
350
- "driveId": "A0AA0A000A000A0A",
351
- "id": "A0AA0A000A000A0A!113",
352
- "name": "folder_a",
353
- "path": "/drive/root:/folder_a",
354
- "siteId": "5f1f11f3-a6b4-4414-aee0-215c774f80db"
355
- },
356
- "file": {
357
- "mimeType": "text/plain",
358
- "hashes": {
359
- "quickXorHash": "79jFLwAAAAAAAAAAAwAAAAAAAAA=",
360
- "sha1Hash": "57218C316B6921E2CD61027A2387EDC31A2D9471",
361
- "sha256Hash": "F1945CD6C19E56B3C1C78943EF5EC18116907A4CA1EFC40A57D48AB1DB7ADFC5"
362
- }
363
- },
364
- "versions@odata.context": "https://graph.microsoft.com/v1.0/$metadata#users('opendal%40outlook.com')/drive/root/versions",
365
- "versions": [
366
- {
367
- "@microsoft.graph.downloadUrl": "https://my.microsoftpersonalcontent.com/personal/A0AA0A000A000A0A/_layouts/15/download.aspx?UniqueId=3b131e1c-7d81-20af-80d0-720000000000&Translate=false&tempauth=v1e.a&ApiVersion=2.0",
368
- "id": "1.0",
369
- "lastModifiedDateTime": "2025-02-16T19:49:05Z",
370
- "size": 3,
371
- "lastModifiedBy": {
372
- "user": {
373
- "email": "erickgdev@outlook.com",
374
- "displayName": "erickgdev@outlook.com"
375
- }
376
- }
377
- }
378
- ]
379
- }"#;
380
-
381
- let item: OneDriveItem = serde_json::from_str(data).unwrap();
382
- let versions = item.versions.expect("Versions present");
383
- assert_eq!("1.0", versions[0].id);
384
- assert_eq!("2025-02-16T19:49:05Z", versions[0].last_modified_date_time);
385
- }
386
-
387
346
  #[test]
388
347
  fn test_parse_one_drive_monitor_status_json() {
389
348
  let data = r#"{
@@ -23,15 +23,14 @@ use opendal_core::raw::oio;
23
23
  use opendal_core::raw::*;
24
24
  use opendal_core::*;
25
25
 
26
- use super::core::OneDriveCore;
27
26
  use super::core::parse_error;
27
+ use super::core::{ErrorContext, OneDriveCore};
28
28
  use super::graph_model::GraphApiOneDriveListResponse;
29
29
  use super::graph_model::ItemType;
30
30
 
31
31
  pub struct OneDriveLister {
32
32
  core: Arc<OneDriveCore>,
33
33
  ctx: OperationContext,
34
- capability: Capability,
35
34
  path: String,
36
35
  op: OpList,
37
36
  }
@@ -43,17 +42,21 @@ impl OneDriveLister {
43
42
  path: String,
44
43
  core: Arc<OneDriveCore>,
45
44
  ctx: OperationContext,
46
- capability: Capability,
47
45
  args: &OpList,
48
46
  ) -> Self {
49
47
  Self {
50
48
  core,
51
49
  ctx,
52
- capability,
53
50
  path,
54
51
  op: args.clone(),
55
52
  }
56
53
  }
54
+
55
+ fn is_after_start(&self, path: &str) -> bool {
56
+ self.op
57
+ .start_after()
58
+ .is_none_or(|start_after| path > start_after)
59
+ }
57
60
  }
58
61
 
59
62
  impl oio::PageList for OneDriveLister {
@@ -74,14 +77,17 @@ impl oio::PageList for OneDriveLister {
74
77
  ctx.done = true;
75
78
  return Ok(());
76
79
  }
77
- return Err(parse_error(response));
80
+ return Err(parse_error(
81
+ ErrorContext::new(ServiceOperation("ListChildren")),
82
+ response,
83
+ ));
78
84
  }
79
85
 
80
86
  let bytes = response.into_body();
81
87
  let decoded_response: GraphApiOneDriveListResponse =
82
88
  serde_json::from_reader(bytes.reader()).map_err(new_json_deserialize_error)?;
83
89
 
84
- let list_with_versions = self.capability.list_with_versions;
90
+ let list_with_versions = self.op.versions();
85
91
 
86
92
  // Include the current directory itself when handling the first page of the listing.
87
93
  if ctx.token.is_empty() && !ctx.done {
@@ -100,8 +106,10 @@ impl oio::PageList for OneDriveLister {
100
106
 
101
107
  // skip `list_with_versions` intentionally because a folder doesn't have versions
102
108
 
103
- let entry = oio::Entry::new(&path, meta);
104
- ctx.entries.push_back(entry);
109
+ if self.is_after_start(&path) {
110
+ let entry = oio::Entry::new(&path, meta);
111
+ ctx.entries.push_back(entry);
112
+ }
105
113
  }
106
114
 
107
115
  if let Some(next_link) = decoded_response.next_link {
@@ -129,25 +137,53 @@ impl oio::PageList for OneDriveLister {
129
137
  normalized_path.push('/');
130
138
  }
131
139
 
132
- let mut meta = Metadata::new(entry_mode)
133
- .with_etag(drive_item.e_tag)
134
- .with_content_length(drive_item.size.max(0) as u64);
140
+ if !self.is_after_start(&normalized_path) {
141
+ continue;
142
+ }
143
+
144
+ let mut meta = if entry_mode == EntryMode::FILE {
145
+ MetadataBuilder::file(drive_item.size.max(0) as u64)
146
+ } else {
147
+ MetadataBuilder::dir()
148
+ };
149
+ meta.etag(drive_item.e_tag);
135
150
  let last_modified = drive_item.last_modified_date_time.parse::<Timestamp>()?;
136
- meta.set_last_modified(last_modified);
137
-
138
- // When listing a directory with `$expand=versions`, OneDrive returns 400 "Operation not supported".
139
- // Thus, `list_with_versions` induces N+1 requests. This N+1 is intentional.
140
- // N+1 is horrendous but we can't do any better without OneDrive's API support.
141
- // When OneDrive supports listing with versions API, remove this.
142
- if list_with_versions {
143
- let versions = self.core.onedrive_list_versions(&self.ctx, &path).await?;
144
- if let Some(version) = versions.first() {
145
- meta.set_version(&version.id);
151
+ meta.last_modified(last_modified);
152
+ let meta = meta.build();
153
+
154
+ // OneDrive doesn't support expanding versions while listing a directory.
155
+ // Query each file separately when callers request versions.
156
+ if list_with_versions && entry_mode == EntryMode::FILE {
157
+ let versions = self
158
+ .core
159
+ .onedrive_list_versions(&self.ctx, &normalized_path)
160
+ .await?;
161
+
162
+ if versions.is_empty() {
163
+ ctx.entries
164
+ .push_back(oio::Entry::new(&normalized_path, meta));
165
+ continue;
146
166
  }
147
- }
148
167
 
149
- let entry = oio::Entry::new(&normalized_path, meta);
150
- ctx.entries.push_back(entry)
168
+ for (index, version) in versions.into_iter().enumerate() {
169
+ let mut version_meta = MetadataBuilder::file(version.size.max(0) as u64);
170
+ version_meta
171
+ .last_modified(version.last_modified_date_time.parse::<Timestamp>()?);
172
+ version_meta.version(&version.id);
173
+ version_meta.is_current(Some(index == 0));
174
+
175
+ // OneDrive exposes the ETag only for the current item.
176
+ if index == 0 {
177
+ version_meta.etag(meta.etag().unwrap_or_default());
178
+ }
179
+
180
+ ctx.entries
181
+ .push_back(oio::Entry::new(&normalized_path, version_meta.build()));
182
+ }
183
+ } else {
184
+ ctx.entries
185
+ .push_back(oio::Entry::new(&normalized_path, meta));
186
+ }
151
187
  }
152
188
 
153
189
  Ok(())