opendal 0.1.6.pre.rc.1

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 (809) hide show
  1. checksums.yaml +7 -0
  2. data/.standard.yml +20 -0
  3. data/.tool-versions +1 -0
  4. data/.yardopts +1 -0
  5. data/Cargo.toml +65 -0
  6. data/DEPENDENCIES.md +9 -0
  7. data/DEPENDENCIES.rust.tsv +277 -0
  8. data/Gemfile +35 -0
  9. data/README.md +159 -0
  10. data/Rakefile +149 -0
  11. data/build.rs +22 -0
  12. data/core/CHANGELOG.md +4929 -0
  13. data/core/CONTRIBUTING.md +61 -0
  14. data/core/Cargo.lock +10259 -0
  15. data/core/Cargo.toml +437 -0
  16. data/core/DEPENDENCIES.md +3 -0
  17. data/core/DEPENDENCIES.rust.tsv +185 -0
  18. data/core/LICENSE +201 -0
  19. data/core/README.md +228 -0
  20. data/core/benches/README.md +18 -0
  21. data/core/benches/ops/README.md +26 -0
  22. data/core/benches/ops/main.rs +25 -0
  23. data/core/benches/ops/read.rs +100 -0
  24. data/core/benches/ops/utils.rs +59 -0
  25. data/core/benches/ops/write.rs +106 -0
  26. data/core/benches/types/README.md +9 -0
  27. data/core/benches/types/buffer.rs +114 -0
  28. data/core/benches/types/main.rs +23 -0
  29. data/core/benches/types/tasks.rs +64 -0
  30. data/core/benches/vs_fs/Cargo.toml +32 -0
  31. data/core/benches/vs_fs/README.md +35 -0
  32. data/core/benches/vs_fs/src/main.rs +83 -0
  33. data/core/benches/vs_s3/Cargo.toml +38 -0
  34. data/core/benches/vs_s3/README.md +55 -0
  35. data/core/benches/vs_s3/src/main.rs +123 -0
  36. data/core/edge/README.md +3 -0
  37. data/core/edge/file_write_on_full_disk/Cargo.toml +31 -0
  38. data/core/edge/file_write_on_full_disk/README.md +14 -0
  39. data/core/edge/file_write_on_full_disk/src/main.rs +43 -0
  40. data/core/edge/s3_aws_assume_role_with_web_identity/Cargo.toml +30 -0
  41. data/core/edge/s3_aws_assume_role_with_web_identity/README.md +18 -0
  42. data/core/edge/s3_aws_assume_role_with_web_identity/src/main.rs +34 -0
  43. data/core/edge/s3_read_on_wasm/.gitignore +3 -0
  44. data/core/edge/s3_read_on_wasm/Cargo.toml +38 -0
  45. data/core/edge/s3_read_on_wasm/README.md +42 -0
  46. data/core/edge/s3_read_on_wasm/src/lib.rs +60 -0
  47. data/core/edge/s3_read_on_wasm/webdriver.json +15 -0
  48. data/core/examples/README.md +23 -0
  49. data/core/examples/basic/Cargo.toml +29 -0
  50. data/core/examples/basic/README.md +15 -0
  51. data/core/examples/basic/src/main.rs +51 -0
  52. data/core/examples/concurrent-upload/Cargo.toml +29 -0
  53. data/core/examples/concurrent-upload/README.md +15 -0
  54. data/core/examples/concurrent-upload/src/main.rs +68 -0
  55. data/core/examples/multipart-upload/Cargo.toml +29 -0
  56. data/core/examples/multipart-upload/README.md +15 -0
  57. data/core/examples/multipart-upload/src/main.rs +56 -0
  58. data/core/fuzz/.gitignore +5 -0
  59. data/core/fuzz/Cargo.toml +92 -0
  60. data/core/fuzz/README.md +68 -0
  61. data/core/fuzz/fuzz_reader.rs +102 -0
  62. data/core/fuzz/fuzz_writer.rs +123 -0
  63. data/core/src/blocking/delete.rs +74 -0
  64. data/core/src/blocking/list.rs +71 -0
  65. data/core/src/blocking/mod.rs +33 -0
  66. data/core/src/blocking/operator.rs +729 -0
  67. data/core/src/blocking/read/buffer_iterator.rs +66 -0
  68. data/core/src/blocking/read/mod.rs +27 -0
  69. data/core/src/blocking/read/reader.rs +124 -0
  70. data/core/src/blocking/read/std_bytes_iterator.rs +69 -0
  71. data/core/src/blocking/read/std_reader.rs +95 -0
  72. data/core/src/blocking/write/mod.rs +22 -0
  73. data/core/src/blocking/write/std_writer.rs +82 -0
  74. data/core/src/blocking/write/writer.rs +109 -0
  75. data/core/src/docs/comparisons/mod.rs +30 -0
  76. data/core/src/docs/comparisons/vs_object_store.md +183 -0
  77. data/core/src/docs/concepts.rs +135 -0
  78. data/core/src/docs/internals/accessor.rs +306 -0
  79. data/core/src/docs/internals/layer.rs +42 -0
  80. data/core/src/docs/internals/mod.rs +62 -0
  81. data/core/src/docs/mod.rs +43 -0
  82. data/core/src/docs/performance/concurrent_write.md +101 -0
  83. data/core/src/docs/performance/http_optimization.md +124 -0
  84. data/core/src/docs/performance/mod.rs +32 -0
  85. data/core/src/docs/rfcs/0000_example.md +74 -0
  86. data/core/src/docs/rfcs/0000_foyer_integration.md +111 -0
  87. data/core/src/docs/rfcs/0041_object_native_api.md +185 -0
  88. data/core/src/docs/rfcs/0044_error_handle.md +198 -0
  89. data/core/src/docs/rfcs/0057_auto_region.md +160 -0
  90. data/core/src/docs/rfcs/0069_object_stream.md +145 -0
  91. data/core/src/docs/rfcs/0090_limited_reader.md +155 -0
  92. data/core/src/docs/rfcs/0112_path_normalization.md +79 -0
  93. data/core/src/docs/rfcs/0191_async_streaming_io.md +328 -0
  94. data/core/src/docs/rfcs/0203_remove_credential.md +96 -0
  95. data/core/src/docs/rfcs/0221_create_dir.md +89 -0
  96. data/core/src/docs/rfcs/0247_retryable_error.md +87 -0
  97. data/core/src/docs/rfcs/0293_object_id.md +67 -0
  98. data/core/src/docs/rfcs/0337_dir_entry.md +191 -0
  99. data/core/src/docs/rfcs/0409_accessor_capabilities.md +67 -0
  100. data/core/src/docs/rfcs/0413_presign.md +154 -0
  101. data/core/src/docs/rfcs/0423_command_line_interface.md +268 -0
  102. data/core/src/docs/rfcs/0429_init_from_iter.md +107 -0
  103. data/core/src/docs/rfcs/0438_multipart.md +163 -0
  104. data/core/src/docs/rfcs/0443_gateway.md +73 -0
  105. data/core/src/docs/rfcs/0501_new_builder.md +111 -0
  106. data/core/src/docs/rfcs/0554_write_refactor.md +96 -0
  107. data/core/src/docs/rfcs/0561_list_metadata_reuse.md +210 -0
  108. data/core/src/docs/rfcs/0599_blocking_api.md +157 -0
  109. data/core/src/docs/rfcs/0623_redis_service.md +300 -0
  110. data/core/src/docs/rfcs/0627_split_capabilities.md +89 -0
  111. data/core/src/docs/rfcs/0661_path_in_accessor.md +126 -0
  112. data/core/src/docs/rfcs/0793_generic_kv_services.md +209 -0
  113. data/core/src/docs/rfcs/0926_object_reader.md +93 -0
  114. data/core/src/docs/rfcs/0977_refactor_error.md +151 -0
  115. data/core/src/docs/rfcs/1085_object_handler.md +73 -0
  116. data/core/src/docs/rfcs/1391_object_metadataer.md +110 -0
  117. data/core/src/docs/rfcs/1398_query_based_metadata.md +125 -0
  118. data/core/src/docs/rfcs/1420_object_writer.md +147 -0
  119. data/core/src/docs/rfcs/1477_remove_object_concept.md +159 -0
  120. data/core/src/docs/rfcs/1735_operation_extension.md +117 -0
  121. data/core/src/docs/rfcs/2083_writer_sink_api.md +106 -0
  122. data/core/src/docs/rfcs/2133_append_api.md +88 -0
  123. data/core/src/docs/rfcs/2299_chain_based_operator_api.md +99 -0
  124. data/core/src/docs/rfcs/2602_object_versioning.md +138 -0
  125. data/core/src/docs/rfcs/2758_merge_append_into_write.md +79 -0
  126. data/core/src/docs/rfcs/2774_lister_api.md +66 -0
  127. data/core/src/docs/rfcs/2779_list_with_metakey.md +143 -0
  128. data/core/src/docs/rfcs/2852_native_capability.md +58 -0
  129. data/core/src/docs/rfcs/2884_merge_range_read_into_read.md +80 -0
  130. data/core/src/docs/rfcs/3017_remove_write_copy_from.md +94 -0
  131. data/core/src/docs/rfcs/3197_config.md +237 -0
  132. data/core/src/docs/rfcs/3232_align_list_api.md +69 -0
  133. data/core/src/docs/rfcs/3243_list_prefix.md +128 -0
  134. data/core/src/docs/rfcs/3356_lazy_reader.md +111 -0
  135. data/core/src/docs/rfcs/3526_list_recursive.md +59 -0
  136. data/core/src/docs/rfcs/3574_concurrent_stat_in_list.md +80 -0
  137. data/core/src/docs/rfcs/3734_buffered_reader.md +64 -0
  138. data/core/src/docs/rfcs/3898_concurrent_writer.md +66 -0
  139. data/core/src/docs/rfcs/3911_deleter_api.md +165 -0
  140. data/core/src/docs/rfcs/4382_range_based_read.md +213 -0
  141. data/core/src/docs/rfcs/4638_executor.md +215 -0
  142. data/core/src/docs/rfcs/5314_remove_metakey.md +120 -0
  143. data/core/src/docs/rfcs/5444_operator_from_uri.md +162 -0
  144. data/core/src/docs/rfcs/5479_context.md +140 -0
  145. data/core/src/docs/rfcs/5485_conditional_reader.md +112 -0
  146. data/core/src/docs/rfcs/5495_list_with_deleted.md +81 -0
  147. data/core/src/docs/rfcs/5556_write_returns_metadata.md +121 -0
  148. data/core/src/docs/rfcs/5871_read_returns_metadata.md +112 -0
  149. data/core/src/docs/rfcs/6189_remove_native_blocking.md +106 -0
  150. data/core/src/docs/rfcs/6209_glob_support.md +132 -0
  151. data/core/src/docs/rfcs/6213_options_api.md +142 -0
  152. data/core/src/docs/rfcs/README.md +62 -0
  153. data/core/src/docs/rfcs/mod.rs +278 -0
  154. data/core/src/docs/upgrade.md +1556 -0
  155. data/core/src/layers/async_backtrace.rs +174 -0
  156. data/core/src/layers/await_tree.rs +202 -0
  157. data/core/src/layers/capability_check.rs +239 -0
  158. data/core/src/layers/chaos.rs +170 -0
  159. data/core/src/layers/complete.rs +385 -0
  160. data/core/src/layers/concurrent_limit.rs +322 -0
  161. data/core/src/layers/correctness_check.rs +440 -0
  162. data/core/src/layers/dtrace.rs +294 -0
  163. data/core/src/layers/error_context.rs +310 -0
  164. data/core/src/layers/fastmetrics.rs +525 -0
  165. data/core/src/layers/fastrace.rs +271 -0
  166. data/core/src/layers/http_client.rs +206 -0
  167. data/core/src/layers/immutable_index.rs +408 -0
  168. data/core/src/layers/logging.rs +842 -0
  169. data/core/src/layers/metrics.rs +182 -0
  170. data/core/src/layers/mime_guess.rs +199 -0
  171. data/core/src/layers/mod.rs +130 -0
  172. data/core/src/layers/observe/metrics.rs +936 -0
  173. data/core/src/layers/observe/mod.rs +93 -0
  174. data/core/src/layers/otelmetrics.rs +496 -0
  175. data/core/src/layers/oteltrace.rs +203 -0
  176. data/core/src/layers/prometheus.rs +686 -0
  177. data/core/src/layers/prometheus_client.rs +519 -0
  178. data/core/src/layers/retry.rs +933 -0
  179. data/core/src/layers/throttle.rs +204 -0
  180. data/core/src/layers/timeout.rs +513 -0
  181. data/core/src/layers/tracing.rs +349 -0
  182. data/core/src/layers/type_eraser.rs +91 -0
  183. data/core/src/lib.rs +204 -0
  184. data/core/src/raw/accessor.rs +856 -0
  185. data/core/src/raw/adapters/kv/api.rs +164 -0
  186. data/core/src/raw/adapters/kv/backend.rs +253 -0
  187. data/core/src/raw/adapters/kv/mod.rs +31 -0
  188. data/core/src/raw/adapters/mod.rs +50 -0
  189. data/core/src/raw/adapters/typed_kv/api.rs +171 -0
  190. data/core/src/raw/adapters/typed_kv/backend.rs +279 -0
  191. data/core/src/raw/adapters/typed_kv/mod.rs +29 -0
  192. data/core/src/raw/atomic_util.rs +57 -0
  193. data/core/src/raw/azure.rs +570 -0
  194. data/core/src/raw/chrono_util.rs +109 -0
  195. data/core/src/raw/enum_utils.rs +201 -0
  196. data/core/src/raw/futures_util.rs +470 -0
  197. data/core/src/raw/http_util/body.rs +144 -0
  198. data/core/src/raw/http_util/bytes_content_range.rs +239 -0
  199. data/core/src/raw/http_util/bytes_range.rs +260 -0
  200. data/core/src/raw/http_util/client.rs +276 -0
  201. data/core/src/raw/http_util/error.rs +68 -0
  202. data/core/src/raw/http_util/header.rs +356 -0
  203. data/core/src/raw/http_util/mod.rs +78 -0
  204. data/core/src/raw/http_util/multipart.rs +1180 -0
  205. data/core/src/raw/http_util/uri.rs +190 -0
  206. data/core/src/raw/layer.rs +295 -0
  207. data/core/src/raw/mod.rs +101 -0
  208. data/core/src/raw/oio/buf/flex_buf.rs +118 -0
  209. data/core/src/raw/oio/buf/mod.rs +25 -0
  210. data/core/src/raw/oio/buf/pooled_buf.rs +126 -0
  211. data/core/src/raw/oio/buf/queue_buf.rs +117 -0
  212. data/core/src/raw/oio/delete/api.rs +102 -0
  213. data/core/src/raw/oio/delete/batch_delete.rs +127 -0
  214. data/core/src/raw/oio/delete/mod.rs +30 -0
  215. data/core/src/raw/oio/delete/one_shot_delete.rs +79 -0
  216. data/core/src/raw/oio/entry.rs +89 -0
  217. data/core/src/raw/oio/list/api.rs +69 -0
  218. data/core/src/raw/oio/list/flat_list.rs +137 -0
  219. data/core/src/raw/oio/list/hierarchy_list.rs +135 -0
  220. data/core/src/raw/oio/list/mod.rs +35 -0
  221. data/core/src/raw/oio/list/page_list.rs +105 -0
  222. data/core/src/raw/oio/list/prefix_list.rs +64 -0
  223. data/core/src/raw/oio/mod.rs +40 -0
  224. data/core/src/raw/oio/read/api.rs +119 -0
  225. data/core/src/raw/oio/read/mod.rs +21 -0
  226. data/core/src/raw/oio/write/api.rs +103 -0
  227. data/core/src/raw/oio/write/append_write.rs +111 -0
  228. data/core/src/raw/oio/write/block_write.rs +405 -0
  229. data/core/src/raw/oio/write/mod.rs +42 -0
  230. data/core/src/raw/oio/write/multipart_write.rs +518 -0
  231. data/core/src/raw/oio/write/one_shot_write.rs +77 -0
  232. data/core/src/raw/oio/write/position_write.rs +284 -0
  233. data/core/src/raw/operation.rs +88 -0
  234. data/core/src/raw/ops.rs +917 -0
  235. data/core/src/raw/path.rs +451 -0
  236. data/core/src/raw/path_cache.rs +244 -0
  237. data/core/src/raw/rps.rs +249 -0
  238. data/core/src/raw/serde_util.rs +423 -0
  239. data/core/src/raw/std_io_util.rs +65 -0
  240. data/core/src/raw/tests/mod.rs +30 -0
  241. data/core/src/raw/tests/read.rs +116 -0
  242. data/core/src/raw/tests/utils.rs +80 -0
  243. data/core/src/raw/tests/write.rs +79 -0
  244. data/core/src/raw/tokio_util.rs +24 -0
  245. data/core/src/raw/version.rs +19 -0
  246. data/core/src/services/aliyun_drive/backend.rs +421 -0
  247. data/core/src/services/aliyun_drive/config.rs +72 -0
  248. data/core/src/services/aliyun_drive/core.rs +651 -0
  249. data/core/src/services/aliyun_drive/delete.rs +51 -0
  250. data/core/src/services/aliyun_drive/docs.md +61 -0
  251. data/core/src/services/aliyun_drive/error.rs +56 -0
  252. data/core/src/services/aliyun_drive/lister.rs +134 -0
  253. data/core/src/services/aliyun_drive/mod.rs +39 -0
  254. data/core/src/services/aliyun_drive/writer.rs +114 -0
  255. data/core/src/services/alluxio/backend.rs +257 -0
  256. data/core/src/services/alluxio/config.rs +50 -0
  257. data/core/src/services/alluxio/core.rs +367 -0
  258. data/core/src/services/alluxio/delete.rs +38 -0
  259. data/core/src/services/alluxio/docs.md +45 -0
  260. data/core/src/services/alluxio/error.rs +99 -0
  261. data/core/src/services/alluxio/lister.rs +73 -0
  262. data/core/src/services/alluxio/mod.rs +39 -0
  263. data/core/src/services/alluxio/writer.rs +74 -0
  264. data/core/src/services/azblob/backend.rs +594 -0
  265. data/core/src/services/azblob/config.rs +220 -0
  266. data/core/src/services/azblob/core.rs +937 -0
  267. data/core/src/services/azblob/delete.rs +108 -0
  268. data/core/src/services/azblob/docs.md +77 -0
  269. data/core/src/services/azblob/error.rs +164 -0
  270. data/core/src/services/azblob/lister.rs +107 -0
  271. data/core/src/services/azblob/mod.rs +38 -0
  272. data/core/src/services/azblob/writer.rs +177 -0
  273. data/core/src/services/azdls/backend.rs +435 -0
  274. data/core/src/services/azdls/config.rs +89 -0
  275. data/core/src/services/azdls/core.rs +388 -0
  276. data/core/src/services/azdls/delete.rs +48 -0
  277. data/core/src/services/azdls/docs.md +73 -0
  278. data/core/src/services/azdls/error.rs +107 -0
  279. data/core/src/services/azdls/lister.rs +165 -0
  280. data/core/src/services/azdls/mod.rs +38 -0
  281. data/core/src/services/azdls/writer.rs +129 -0
  282. data/core/src/services/azfile/backend.rs +373 -0
  283. data/core/src/services/azfile/config.rs +61 -0
  284. data/core/src/services/azfile/core.rs +435 -0
  285. data/core/src/services/azfile/delete.rs +51 -0
  286. data/core/src/services/azfile/docs.md +65 -0
  287. data/core/src/services/azfile/error.rs +108 -0
  288. data/core/src/services/azfile/lister.rs +217 -0
  289. data/core/src/services/azfile/mod.rs +39 -0
  290. data/core/src/services/azfile/writer.rs +92 -0
  291. data/core/src/services/b2/backend.rs +434 -0
  292. data/core/src/services/b2/config.rs +64 -0
  293. data/core/src/services/b2/core.rs +742 -0
  294. data/core/src/services/b2/delete.rs +56 -0
  295. data/core/src/services/b2/docs.md +54 -0
  296. data/core/src/services/b2/error.rs +132 -0
  297. data/core/src/services/b2/lister.rs +110 -0
  298. data/core/src/services/b2/mod.rs +39 -0
  299. data/core/src/services/b2/writer.rs +189 -0
  300. data/core/src/services/cacache/backend.rs +160 -0
  301. data/core/src/services/cacache/config.rs +28 -0
  302. data/core/src/services/cacache/core.rs +96 -0
  303. data/core/src/services/cacache/delete.rs +39 -0
  304. data/core/src/services/cacache/docs.md +38 -0
  305. data/core/src/services/cacache/mod.rs +34 -0
  306. data/core/src/services/cacache/writer.rs +61 -0
  307. data/core/src/services/cloudflare_kv/backend.rs +513 -0
  308. data/core/src/services/cloudflare_kv/config.rs +55 -0
  309. data/core/src/services/cloudflare_kv/core.rs +168 -0
  310. data/core/src/services/cloudflare_kv/delete.rs +119 -0
  311. data/core/src/services/cloudflare_kv/docs.md +21 -0
  312. data/core/src/services/cloudflare_kv/error.rs +79 -0
  313. data/core/src/services/cloudflare_kv/lister.rs +170 -0
  314. data/core/src/services/cloudflare_kv/mod.rs +39 -0
  315. data/core/src/services/cloudflare_kv/model.rs +76 -0
  316. data/core/src/services/cloudflare_kv/writer.rs +68 -0
  317. data/core/src/services/compfs/backend.rs +290 -0
  318. data/core/src/services/compfs/config.rs +30 -0
  319. data/core/src/services/compfs/core.rs +159 -0
  320. data/core/src/services/compfs/delete.rs +53 -0
  321. data/core/src/services/compfs/lister.rs +98 -0
  322. data/core/src/services/compfs/mod.rs +38 -0
  323. data/core/src/services/compfs/reader.rs +79 -0
  324. data/core/src/services/compfs/writer.rs +90 -0
  325. data/core/src/services/cos/backend.rs +442 -0
  326. data/core/src/services/cos/config.rs +54 -0
  327. data/core/src/services/cos/core.rs +761 -0
  328. data/core/src/services/cos/delete.rs +48 -0
  329. data/core/src/services/cos/docs.md +55 -0
  330. data/core/src/services/cos/error.rs +105 -0
  331. data/core/src/services/cos/lister.rs +237 -0
  332. data/core/src/services/cos/mod.rs +39 -0
  333. data/core/src/services/cos/writer.rs +234 -0
  334. data/core/src/services/d1/backend.rs +330 -0
  335. data/core/src/services/d1/config.rs +55 -0
  336. data/core/src/services/d1/docs.md +48 -0
  337. data/core/src/services/d1/error.rs +79 -0
  338. data/core/src/services/d1/mod.rs +29 -0
  339. data/core/src/services/d1/model.rs +125 -0
  340. data/core/src/services/dashmap/backend.rs +203 -0
  341. data/core/src/services/dashmap/config.rs +37 -0
  342. data/core/src/services/dashmap/core.rs +61 -0
  343. data/core/src/services/dashmap/delete.rs +40 -0
  344. data/core/src/services/dashmap/docs.md +38 -0
  345. data/core/src/services/dashmap/lister.rs +63 -0
  346. data/core/src/services/dashmap/mod.rs +36 -0
  347. data/core/src/services/dashmap/writer.rs +87 -0
  348. data/core/src/services/dbfs/backend.rs +258 -0
  349. data/core/src/services/dbfs/config.rs +48 -0
  350. data/core/src/services/dbfs/core.rs +191 -0
  351. data/core/src/services/dbfs/delete.rs +49 -0
  352. data/core/src/services/dbfs/docs.md +57 -0
  353. data/core/src/services/dbfs/error.rs +74 -0
  354. data/core/src/services/dbfs/lister.rs +96 -0
  355. data/core/src/services/dbfs/mod.rs +38 -0
  356. data/core/src/services/dbfs/writer.rs +64 -0
  357. data/core/src/services/dropbox/backend.rs +187 -0
  358. data/core/src/services/dropbox/builder.rs +222 -0
  359. data/core/src/services/dropbox/config.rs +47 -0
  360. data/core/src/services/dropbox/core.rs +496 -0
  361. data/core/src/services/dropbox/delete.rs +54 -0
  362. data/core/src/services/dropbox/docs.md +64 -0
  363. data/core/src/services/dropbox/error.rs +85 -0
  364. data/core/src/services/dropbox/lister.rs +117 -0
  365. data/core/src/services/dropbox/mod.rs +40 -0
  366. data/core/src/services/dropbox/writer.rs +51 -0
  367. data/core/src/services/etcd/backend.rs +345 -0
  368. data/core/src/services/etcd/config.rs +86 -0
  369. data/core/src/services/etcd/core.rs +143 -0
  370. data/core/src/services/etcd/deleter.rs +41 -0
  371. data/core/src/services/etcd/docs.md +45 -0
  372. data/core/src/services/etcd/error.rs +26 -0
  373. data/core/src/services/etcd/lister.rs +79 -0
  374. data/core/src/services/etcd/mod.rs +36 -0
  375. data/core/src/services/etcd/writer.rs +61 -0
  376. data/core/src/services/foundationdb/backend.rs +171 -0
  377. data/core/src/services/foundationdb/config.rs +45 -0
  378. data/core/src/services/foundationdb/docs.md +42 -0
  379. data/core/src/services/foundationdb/mod.rs +24 -0
  380. data/core/src/services/fs/backend.rs +299 -0
  381. data/core/src/services/fs/config.rs +33 -0
  382. data/core/src/services/fs/core.rs +227 -0
  383. data/core/src/services/fs/delete.rs +53 -0
  384. data/core/src/services/fs/docs.md +49 -0
  385. data/core/src/services/fs/error.rs +31 -0
  386. data/core/src/services/fs/lister.rs +81 -0
  387. data/core/src/services/fs/mod.rs +40 -0
  388. data/core/src/services/fs/reader.rs +83 -0
  389. data/core/src/services/fs/writer.rs +212 -0
  390. data/core/src/services/ftp/backend.rs +388 -0
  391. data/core/src/services/ftp/config.rs +46 -0
  392. data/core/src/services/ftp/core.rs +136 -0
  393. data/core/src/services/ftp/delete.rs +62 -0
  394. data/core/src/services/ftp/docs.md +42 -0
  395. data/core/src/services/ftp/err.rs +47 -0
  396. data/core/src/services/ftp/lister.rs +72 -0
  397. data/core/src/services/ftp/mod.rs +41 -0
  398. data/core/src/services/ftp/reader.rs +84 -0
  399. data/core/src/services/ftp/writer.rs +122 -0
  400. data/core/src/services/gcs/backend.rs +499 -0
  401. data/core/src/services/gcs/config.rs +168 -0
  402. data/core/src/services/gcs/core.rs +1079 -0
  403. data/core/src/services/gcs/delete.rs +98 -0
  404. data/core/src/services/gcs/docs.md +76 -0
  405. data/core/src/services/gcs/error.rs +122 -0
  406. data/core/src/services/gcs/lister.rs +136 -0
  407. data/core/src/services/gcs/mod.rs +40 -0
  408. data/core/src/services/gcs/uri.rs +75 -0
  409. data/core/src/services/gcs/writer.rs +163 -0
  410. data/core/src/services/gdrive/backend.rs +176 -0
  411. data/core/src/services/gdrive/builder.rs +228 -0
  412. data/core/src/services/gdrive/config.rs +47 -0
  413. data/core/src/services/gdrive/core.rs +499 -0
  414. data/core/src/services/gdrive/delete.rs +57 -0
  415. data/core/src/services/gdrive/docs.md +65 -0
  416. data/core/src/services/gdrive/error.rs +80 -0
  417. data/core/src/services/gdrive/lister.rs +110 -0
  418. data/core/src/services/gdrive/mod.rs +40 -0
  419. data/core/src/services/gdrive/writer.rs +77 -0
  420. data/core/src/services/ghac/backend.rs +285 -0
  421. data/core/src/services/ghac/config.rs +36 -0
  422. data/core/src/services/ghac/core.rs +459 -0
  423. data/core/src/services/ghac/docs.md +84 -0
  424. data/core/src/services/ghac/error.rs +52 -0
  425. data/core/src/services/ghac/mod.rs +35 -0
  426. data/core/src/services/ghac/writer.rs +201 -0
  427. data/core/src/services/github/backend.rs +285 -0
  428. data/core/src/services/github/config.rs +59 -0
  429. data/core/src/services/github/core.rs +351 -0
  430. data/core/src/services/github/delete.rs +41 -0
  431. data/core/src/services/github/docs.md +52 -0
  432. data/core/src/services/github/error.rs +101 -0
  433. data/core/src/services/github/lister.rs +112 -0
  434. data/core/src/services/github/mod.rs +38 -0
  435. data/core/src/services/github/writer.rs +51 -0
  436. data/core/src/services/gridfs/backend.rs +166 -0
  437. data/core/src/services/gridfs/config.rs +50 -0
  438. data/core/src/services/gridfs/core.rs +154 -0
  439. data/core/src/services/gridfs/docs.md +46 -0
  440. data/core/src/services/gridfs/mod.rs +26 -0
  441. data/core/src/services/hdfs/backend.rs +413 -0
  442. data/core/src/services/hdfs/config.rs +59 -0
  443. data/core/src/services/hdfs/delete.rs +62 -0
  444. data/core/src/services/hdfs/docs.md +140 -0
  445. data/core/src/services/hdfs/lister.rs +70 -0
  446. data/core/src/services/hdfs/mod.rs +36 -0
  447. data/core/src/services/hdfs/reader.rs +79 -0
  448. data/core/src/services/hdfs/writer.rs +104 -0
  449. data/core/src/services/hdfs_native/backend.rs +340 -0
  450. data/core/src/services/hdfs_native/config.rs +45 -0
  451. data/core/src/services/hdfs_native/delete.rs +47 -0
  452. data/core/src/services/hdfs_native/docs.md +35 -0
  453. data/core/src/services/hdfs_native/error.rs +59 -0
  454. data/core/src/services/hdfs_native/lister.rs +85 -0
  455. data/core/src/services/hdfs_native/mod.rs +39 -0
  456. data/core/src/services/hdfs_native/reader.rs +62 -0
  457. data/core/src/services/hdfs_native/writer.rs +61 -0
  458. data/core/src/services/http/backend.rs +291 -0
  459. data/core/src/services/http/config.rs +49 -0
  460. data/core/src/services/http/core.rs +125 -0
  461. data/core/src/services/http/docs.md +45 -0
  462. data/core/src/services/http/error.rs +53 -0
  463. data/core/src/services/http/mod.rs +32 -0
  464. data/core/src/services/huggingface/backend.rs +289 -0
  465. data/core/src/services/huggingface/config.rs +75 -0
  466. data/core/src/services/huggingface/core.rs +406 -0
  467. data/core/src/services/huggingface/docs.md +61 -0
  468. data/core/src/services/huggingface/error.rs +93 -0
  469. data/core/src/services/huggingface/lister.rs +91 -0
  470. data/core/src/services/huggingface/mod.rs +34 -0
  471. data/core/src/services/ipfs/backend.rs +257 -0
  472. data/core/src/services/ipfs/config.rs +32 -0
  473. data/core/src/services/ipfs/core.rs +239 -0
  474. data/core/src/services/ipfs/docs.md +45 -0
  475. data/core/src/services/ipfs/error.rs +52 -0
  476. data/core/src/services/ipfs/ipld.rs +162 -0
  477. data/core/src/services/ipfs/mod.rs +34 -0
  478. data/core/src/services/ipmfs/backend.rs +147 -0
  479. data/core/src/services/ipmfs/builder.rs +166 -0
  480. data/core/src/services/ipmfs/config.rs +32 -0
  481. data/core/src/services/ipmfs/core.rs +142 -0
  482. data/core/src/services/ipmfs/delete.rs +48 -0
  483. data/core/src/services/ipmfs/docs.md +14 -0
  484. data/core/src/services/ipmfs/error.rs +83 -0
  485. data/core/src/services/ipmfs/lister.rs +135 -0
  486. data/core/src/services/ipmfs/mod.rs +40 -0
  487. data/core/src/services/ipmfs/writer.rs +49 -0
  488. data/core/src/services/koofr/backend.rs +361 -0
  489. data/core/src/services/koofr/config.rs +50 -0
  490. data/core/src/services/koofr/core.rs +458 -0
  491. data/core/src/services/koofr/delete.rs +50 -0
  492. data/core/src/services/koofr/docs.md +51 -0
  493. data/core/src/services/koofr/error.rs +72 -0
  494. data/core/src/services/koofr/lister.rs +88 -0
  495. data/core/src/services/koofr/mod.rs +38 -0
  496. data/core/src/services/koofr/writer.rs +53 -0
  497. data/core/src/services/lakefs/backend.rs +309 -0
  498. data/core/src/services/lakefs/config.rs +81 -0
  499. data/core/src/services/lakefs/core.rs +261 -0
  500. data/core/src/services/lakefs/delete.rs +54 -0
  501. data/core/src/services/lakefs/docs.md +62 -0
  502. data/core/src/services/lakefs/error.rs +93 -0
  503. data/core/src/services/lakefs/lister.rs +120 -0
  504. data/core/src/services/lakefs/mod.rs +38 -0
  505. data/core/src/services/lakefs/writer.rs +50 -0
  506. data/core/src/services/memcached/backend.rs +284 -0
  507. data/core/src/services/memcached/binary.rs +289 -0
  508. data/core/src/services/memcached/config.rs +43 -0
  509. data/core/src/services/memcached/docs.md +47 -0
  510. data/core/src/services/memcached/mod.rs +27 -0
  511. data/core/src/services/memory/backend.rs +205 -0
  512. data/core/src/services/memory/config.rs +30 -0
  513. data/core/src/services/memory/core.rs +80 -0
  514. data/core/src/services/memory/delete.rs +42 -0
  515. data/core/src/services/memory/docs.md +36 -0
  516. data/core/src/services/memory/lister.rs +56 -0
  517. data/core/src/services/memory/mod.rs +36 -0
  518. data/core/src/services/memory/writer.rs +85 -0
  519. data/core/src/services/mini_moka/backend.rs +260 -0
  520. data/core/src/services/mini_moka/config.rs +56 -0
  521. data/core/src/services/mini_moka/core.rs +52 -0
  522. data/core/src/services/mini_moka/delete.rs +42 -0
  523. data/core/src/services/mini_moka/docs.md +19 -0
  524. data/core/src/services/mini_moka/lister.rs +68 -0
  525. data/core/src/services/mini_moka/mod.rs +36 -0
  526. data/core/src/services/mini_moka/writer.rs +84 -0
  527. data/core/src/services/mod.rs +206 -0
  528. data/core/src/services/moka/backend.rs +326 -0
  529. data/core/src/services/moka/config.rs +59 -0
  530. data/core/src/services/moka/core.rs +62 -0
  531. data/core/src/services/moka/delete.rs +42 -0
  532. data/core/src/services/moka/docs.md +42 -0
  533. data/core/src/services/moka/lister.rs +65 -0
  534. data/core/src/services/moka/mod.rs +41 -0
  535. data/core/src/services/moka/writer.rs +83 -0
  536. data/core/src/services/mongodb/backend.rs +291 -0
  537. data/core/src/services/mongodb/config.rs +54 -0
  538. data/core/src/services/mongodb/docs.md +49 -0
  539. data/core/src/services/mongodb/mod.rs +24 -0
  540. data/core/src/services/monoiofs/backend.rs +238 -0
  541. data/core/src/services/monoiofs/config.rs +34 -0
  542. data/core/src/services/monoiofs/core.rs +313 -0
  543. data/core/src/services/monoiofs/delete.rs +64 -0
  544. data/core/src/services/monoiofs/docs.md +46 -0
  545. data/core/src/services/monoiofs/mod.rs +36 -0
  546. data/core/src/services/monoiofs/reader.rs +147 -0
  547. data/core/src/services/monoiofs/writer.rs +189 -0
  548. data/core/src/services/mysql/backend.rs +256 -0
  549. data/core/src/services/mysql/config.rs +66 -0
  550. data/core/src/services/mysql/docs.md +47 -0
  551. data/core/src/services/mysql/mod.rs +24 -0
  552. data/core/src/services/obs/backend.rs +442 -0
  553. data/core/src/services/obs/config.rs +53 -0
  554. data/core/src/services/obs/core.rs +608 -0
  555. data/core/src/services/obs/delete.rs +48 -0
  556. data/core/src/services/obs/docs.md +54 -0
  557. data/core/src/services/obs/error.rs +106 -0
  558. data/core/src/services/obs/lister.rs +101 -0
  559. data/core/src/services/obs/mod.rs +38 -0
  560. data/core/src/services/obs/writer.rs +235 -0
  561. data/core/src/services/onedrive/backend.rs +127 -0
  562. data/core/src/services/onedrive/builder.rs +236 -0
  563. data/core/src/services/onedrive/config.rs +49 -0
  564. data/core/src/services/onedrive/core.rs +691 -0
  565. data/core/src/services/onedrive/delete.rs +47 -0
  566. data/core/src/services/onedrive/docs.md +115 -0
  567. data/core/src/services/onedrive/error.rs +61 -0
  568. data/core/src/services/onedrive/graph_model.rs +425 -0
  569. data/core/src/services/onedrive/lister.rs +150 -0
  570. data/core/src/services/onedrive/mod.rs +42 -0
  571. data/core/src/services/onedrive/writer.rs +168 -0
  572. data/core/src/services/opfs/backend.rs +50 -0
  573. data/core/src/services/opfs/config.rs +25 -0
  574. data/core/src/services/opfs/core.rs +74 -0
  575. data/core/src/services/opfs/docs.md +18 -0
  576. data/core/src/services/opfs/error.rs +27 -0
  577. data/core/src/services/opfs/mod.rs +30 -0
  578. data/core/src/services/opfs/utils.rs +70 -0
  579. data/core/src/services/oss/backend.rs +734 -0
  580. data/core/src/services/oss/config.rs +113 -0
  581. data/core/src/services/oss/core.rs +1088 -0
  582. data/core/src/services/oss/delete.rs +109 -0
  583. data/core/src/services/oss/docs.md +74 -0
  584. data/core/src/services/oss/error.rs +109 -0
  585. data/core/src/services/oss/lister.rs +256 -0
  586. data/core/src/services/oss/mod.rs +38 -0
  587. data/core/src/services/oss/writer.rs +228 -0
  588. data/core/src/services/pcloud/backend.rs +358 -0
  589. data/core/src/services/pcloud/config.rs +51 -0
  590. data/core/src/services/pcloud/core.rs +461 -0
  591. data/core/src/services/pcloud/delete.rs +66 -0
  592. data/core/src/services/pcloud/docs.md +51 -0
  593. data/core/src/services/pcloud/error.rs +88 -0
  594. data/core/src/services/pcloud/lister.rs +95 -0
  595. data/core/src/services/pcloud/mod.rs +38 -0
  596. data/core/src/services/pcloud/writer.rs +66 -0
  597. data/core/src/services/persy/backend.rs +226 -0
  598. data/core/src/services/persy/config.rs +32 -0
  599. data/core/src/services/persy/docs.md +43 -0
  600. data/core/src/services/persy/mod.rs +24 -0
  601. data/core/src/services/postgresql/backend.rs +258 -0
  602. data/core/src/services/postgresql/config.rs +66 -0
  603. data/core/src/services/postgresql/docs.md +47 -0
  604. data/core/src/services/postgresql/mod.rs +24 -0
  605. data/core/src/services/redb/backend.rs +280 -0
  606. data/core/src/services/redb/config.rs +34 -0
  607. data/core/src/services/redb/docs.md +41 -0
  608. data/core/src/services/redb/mod.rs +24 -0
  609. data/core/src/services/redis/backend.rs +442 -0
  610. data/core/src/services/redis/config.rs +79 -0
  611. data/core/src/services/redis/core.rs +209 -0
  612. data/core/src/services/redis/delete.rs +40 -0
  613. data/core/src/services/redis/docs.md +43 -0
  614. data/core/src/services/redis/mod.rs +34 -0
  615. data/core/src/services/redis/writer.rs +57 -0
  616. data/core/src/services/rocksdb/backend.rs +159 -0
  617. data/core/src/services/rocksdb/config.rs +34 -0
  618. data/core/src/services/rocksdb/docs.md +54 -0
  619. data/core/src/services/rocksdb/mod.rs +24 -0
  620. data/core/src/services/s3/backend.rs +1293 -0
  621. data/core/src/services/s3/compatible_services.md +126 -0
  622. data/core/src/services/s3/config.rs +327 -0
  623. data/core/src/services/s3/core.rs +1741 -0
  624. data/core/src/services/s3/delete.rs +109 -0
  625. data/core/src/services/s3/docs.md +244 -0
  626. data/core/src/services/s3/error.rs +171 -0
  627. data/core/src/services/s3/lister.rs +405 -0
  628. data/core/src/services/s3/mod.rs +38 -0
  629. data/core/src/services/s3/writer.rs +262 -0
  630. data/core/src/services/seafile/backend.rs +297 -0
  631. data/core/src/services/seafile/config.rs +56 -0
  632. data/core/src/services/seafile/core.rs +475 -0
  633. data/core/src/services/seafile/delete.rs +40 -0
  634. data/core/src/services/seafile/docs.md +54 -0
  635. data/core/src/services/seafile/error.rs +86 -0
  636. data/core/src/services/seafile/lister.rs +83 -0
  637. data/core/src/services/seafile/mod.rs +38 -0
  638. data/core/src/services/seafile/writer.rs +55 -0
  639. data/core/src/services/sftp/backend.rs +397 -0
  640. data/core/src/services/sftp/config.rs +50 -0
  641. data/core/src/services/sftp/core.rs +154 -0
  642. data/core/src/services/sftp/delete.rs +55 -0
  643. data/core/src/services/sftp/docs.md +49 -0
  644. data/core/src/services/sftp/error.rs +57 -0
  645. data/core/src/services/sftp/lister.rs +88 -0
  646. data/core/src/services/sftp/mod.rs +42 -0
  647. data/core/src/services/sftp/reader.rs +78 -0
  648. data/core/src/services/sftp/utils.rs +51 -0
  649. data/core/src/services/sftp/writer.rs +67 -0
  650. data/core/src/services/sled/backend.rs +194 -0
  651. data/core/src/services/sled/config.rs +45 -0
  652. data/core/src/services/sled/docs.md +39 -0
  653. data/core/src/services/sled/mod.rs +24 -0
  654. data/core/src/services/sqlite/backend.rs +326 -0
  655. data/core/src/services/sqlite/config.rs +70 -0
  656. data/core/src/services/sqlite/docs.md +46 -0
  657. data/core/src/services/sqlite/mod.rs +24 -0
  658. data/core/src/services/surrealdb/backend.rs +365 -0
  659. data/core/src/services/surrealdb/config.rs +64 -0
  660. data/core/src/services/surrealdb/docs.md +54 -0
  661. data/core/src/services/surrealdb/mod.rs +24 -0
  662. data/core/src/services/swift/backend.rs +275 -0
  663. data/core/src/services/swift/compatible_services.md +53 -0
  664. data/core/src/services/swift/config.rs +53 -0
  665. data/core/src/services/swift/core.rs +310 -0
  666. data/core/src/services/swift/delete.rs +49 -0
  667. data/core/src/services/swift/docs.md +52 -0
  668. data/core/src/services/swift/error.rs +90 -0
  669. data/core/src/services/swift/lister.rs +119 -0
  670. data/core/src/services/swift/mod.rs +38 -0
  671. data/core/src/services/swift/writer.rs +53 -0
  672. data/core/src/services/tikv/backend.rs +237 -0
  673. data/core/src/services/tikv/config.rs +52 -0
  674. data/core/src/services/tikv/docs.md +43 -0
  675. data/core/src/services/tikv/mod.rs +24 -0
  676. data/core/src/services/upyun/backend.rs +317 -0
  677. data/core/src/services/upyun/config.rs +51 -0
  678. data/core/src/services/upyun/core.rs +521 -0
  679. data/core/src/services/upyun/delete.rs +50 -0
  680. data/core/src/services/upyun/docs.md +51 -0
  681. data/core/src/services/upyun/error.rs +97 -0
  682. data/core/src/services/upyun/lister.rs +101 -0
  683. data/core/src/services/upyun/mod.rs +38 -0
  684. data/core/src/services/upyun/writer.rs +127 -0
  685. data/core/src/services/vercel_artifacts/backend.rs +99 -0
  686. data/core/src/services/vercel_artifacts/builder.rs +117 -0
  687. data/core/src/services/vercel_artifacts/config.rs +39 -0
  688. data/core/src/services/vercel_artifacts/core.rs +112 -0
  689. data/core/src/services/vercel_artifacts/docs.md +40 -0
  690. data/core/src/services/vercel_artifacts/error.rs +50 -0
  691. data/core/src/services/vercel_artifacts/mod.rs +36 -0
  692. data/core/src/services/vercel_artifacts/writer.rs +58 -0
  693. data/core/src/services/vercel_blob/backend.rs +251 -0
  694. data/core/src/services/vercel_blob/config.rs +45 -0
  695. data/core/src/services/vercel_blob/core.rs +449 -0
  696. data/core/src/services/vercel_blob/delete.rs +38 -0
  697. data/core/src/services/vercel_blob/docs.md +45 -0
  698. data/core/src/services/vercel_blob/error.rs +110 -0
  699. data/core/src/services/vercel_blob/lister.rs +69 -0
  700. data/core/src/services/vercel_blob/mod.rs +38 -0
  701. data/core/src/services/vercel_blob/writer.rs +143 -0
  702. data/core/src/services/webdav/backend.rs +318 -0
  703. data/core/src/services/webdav/config.rs +53 -0
  704. data/core/src/services/webdav/core.rs +859 -0
  705. data/core/src/services/webdav/delete.rs +47 -0
  706. data/core/src/services/webdav/docs.md +49 -0
  707. data/core/src/services/webdav/error.rs +53 -0
  708. data/core/src/services/webdav/lister.rs +106 -0
  709. data/core/src/services/webdav/mod.rs +38 -0
  710. data/core/src/services/webdav/writer.rs +56 -0
  711. data/core/src/services/webhdfs/backend.rs +376 -0
  712. data/core/src/services/webhdfs/config.rs +52 -0
  713. data/core/src/services/webhdfs/core.rs +398 -0
  714. data/core/src/services/webhdfs/delete.rs +46 -0
  715. data/core/src/services/webhdfs/docs.md +90 -0
  716. data/core/src/services/webhdfs/error.rs +126 -0
  717. data/core/src/services/webhdfs/lister.rs +130 -0
  718. data/core/src/services/webhdfs/message.rs +249 -0
  719. data/core/src/services/webhdfs/mod.rs +41 -0
  720. data/core/src/services/webhdfs/writer.rs +177 -0
  721. data/core/src/services/yandex_disk/backend.rs +267 -0
  722. data/core/src/services/yandex_disk/config.rs +45 -0
  723. data/core/src/services/yandex_disk/core.rs +340 -0
  724. data/core/src/services/yandex_disk/delete.rs +54 -0
  725. data/core/src/services/yandex_disk/docs.md +45 -0
  726. data/core/src/services/yandex_disk/error.rs +104 -0
  727. data/core/src/services/yandex_disk/lister.rs +113 -0
  728. data/core/src/services/yandex_disk/mod.rs +38 -0
  729. data/core/src/services/yandex_disk/writer.rs +52 -0
  730. data/core/src/types/buffer.rs +991 -0
  731. data/core/src/types/builder.rs +152 -0
  732. data/core/src/types/capability.rs +209 -0
  733. data/core/src/types/context/mod.rs +22 -0
  734. data/core/src/types/context/read.rs +231 -0
  735. data/core/src/types/context/write.rs +441 -0
  736. data/core/src/types/delete/deleter.rs +220 -0
  737. data/core/src/types/delete/futures_delete_sink.rs +176 -0
  738. data/core/src/types/delete/input.rs +97 -0
  739. data/core/src/types/delete/mod.rs +26 -0
  740. data/core/src/types/entry.rs +69 -0
  741. data/core/src/types/error.rs +570 -0
  742. data/core/src/types/execute/api.rs +110 -0
  743. data/core/src/types/execute/executor.rs +96 -0
  744. data/core/src/types/execute/executors/mod.rs +27 -0
  745. data/core/src/types/execute/executors/tokio_executor.rs +60 -0
  746. data/core/src/types/execute/mod.rs +25 -0
  747. data/core/src/types/list.rs +137 -0
  748. data/core/src/types/metadata.rs +436 -0
  749. data/core/src/types/mod.rs +72 -0
  750. data/core/src/types/mode.rs +68 -0
  751. data/core/src/types/operator/builder.rs +535 -0
  752. data/core/src/types/operator/info.rs +63 -0
  753. data/core/src/types/operator/mod.rs +33 -0
  754. data/core/src/types/operator/operator.rs +2236 -0
  755. data/core/src/types/operator/operator_futures.rs +1430 -0
  756. data/core/src/types/operator/registry.rs +129 -0
  757. data/core/src/types/options.rs +548 -0
  758. data/core/src/types/read/buffer_stream.rs +273 -0
  759. data/core/src/types/read/futures_async_reader.rs +289 -0
  760. data/core/src/types/read/futures_bytes_stream.rs +157 -0
  761. data/core/src/types/read/mod.rs +29 -0
  762. data/core/src/types/read/reader.rs +604 -0
  763. data/core/src/types/scheme.rs +475 -0
  764. data/core/src/types/write/buffer_sink.rs +188 -0
  765. data/core/src/types/write/futures_async_writer.rs +136 -0
  766. data/core/src/types/write/futures_bytes_sink.rs +103 -0
  767. data/core/src/types/write/mod.rs +26 -0
  768. data/core/src/types/write/writer.rs +411 -0
  769. data/core/tests/behavior/README.md +77 -0
  770. data/core/tests/behavior/async_copy.rs +314 -0
  771. data/core/tests/behavior/async_create_dir.rs +53 -0
  772. data/core/tests/behavior/async_delete.rs +354 -0
  773. data/core/tests/behavior/async_list.rs +739 -0
  774. data/core/tests/behavior/async_presign.rs +175 -0
  775. data/core/tests/behavior/async_read.rs +871 -0
  776. data/core/tests/behavior/async_rename.rs +210 -0
  777. data/core/tests/behavior/async_stat.rs +628 -0
  778. data/core/tests/behavior/async_write.rs +819 -0
  779. data/core/tests/behavior/main.rs +78 -0
  780. data/core/tests/behavior/utils.rs +187 -0
  781. data/core/tests/data/normal_dir/.gitkeep +0 -0
  782. data/core/tests/data/normal_file.txt +1041 -0
  783. data/core/tests/data/special_dir !@#$%^&()_+-=;',/.gitkeep +0 -0
  784. data/core/tests/data/special_file !@#$%^&()_+-=;',.txt +1041 -0
  785. data/core/users.md +13 -0
  786. data/extconf.rb +24 -0
  787. data/lib/opendal.rb +25 -0
  788. data/lib/opendal_ruby/entry.rb +35 -0
  789. data/lib/opendal_ruby/io.rb +70 -0
  790. data/lib/opendal_ruby/metadata.rb +44 -0
  791. data/lib/opendal_ruby/operator.rb +29 -0
  792. data/lib/opendal_ruby/operator_info.rb +26 -0
  793. data/src/capability.rs +146 -0
  794. data/src/io.rs +464 -0
  795. data/src/lib.rs +63 -0
  796. data/src/lister.rs +141 -0
  797. data/src/metadata.rs +111 -0
  798. data/src/middlewares.rs +174 -0
  799. data/src/operator.rs +310 -0
  800. data/src/operator_info.rs +83 -0
  801. data/test/blocking_op_test.rb +112 -0
  802. data/test/capability_test.rb +42 -0
  803. data/test/io_test.rb +172 -0
  804. data/test/lister_test.rb +77 -0
  805. data/test/metadata_test.rb +78 -0
  806. data/test/middlewares_test.rb +46 -0
  807. data/test/operator_info_test.rb +35 -0
  808. data/test/test_helper.rb +36 -0
  809. metadata +857 -0
@@ -0,0 +1,1741 @@
1
+ // Licensed to the Apache Software Foundation (ASF) under one
2
+ // or more contributor license agreements. See the NOTICE file
3
+ // distributed with this work for additional information
4
+ // regarding copyright ownership. The ASF licenses this file
5
+ // to you under the Apache License, Version 2.0 (the
6
+ // "License"); you may not use this file except in compliance
7
+ // with the License. You may obtain a copy of the License at
8
+ //
9
+ // http://www.apache.org/licenses/LICENSE-2.0
10
+ //
11
+ // Unless required by applicable law or agreed to in writing,
12
+ // software distributed under the License is distributed on an
13
+ // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14
+ // KIND, either express or implied. See the License for the
15
+ // specific language governing permissions and limitations
16
+ // under the License.
17
+
18
+ use std::fmt;
19
+ use std::fmt::Debug;
20
+ use std::fmt::Display;
21
+ use std::fmt::Formatter;
22
+ use std::fmt::Write;
23
+ use std::sync::atomic;
24
+ use std::sync::atomic::AtomicBool;
25
+ use std::sync::Arc;
26
+ use std::time::Duration;
27
+
28
+ use base64::prelude::BASE64_STANDARD;
29
+ use base64::Engine;
30
+ use bytes::Bytes;
31
+ use constants::X_AMZ_META_PREFIX;
32
+ use http::header::HeaderName;
33
+ use http::header::CACHE_CONTROL;
34
+ use http::header::CONTENT_DISPOSITION;
35
+ use http::header::CONTENT_ENCODING;
36
+ use http::header::CONTENT_LENGTH;
37
+ use http::header::CONTENT_TYPE;
38
+ use http::header::HOST;
39
+ use http::header::IF_MATCH;
40
+ use http::header::IF_MODIFIED_SINCE;
41
+ use http::header::IF_NONE_MATCH;
42
+ use http::header::IF_UNMODIFIED_SINCE;
43
+ use http::HeaderValue;
44
+ use http::Request;
45
+ use http::Response;
46
+ use reqsign::AwsCredential;
47
+ use reqsign::AwsCredentialLoad;
48
+ use reqsign::AwsV4Signer;
49
+ use serde::Deserialize;
50
+ use serde::Serialize;
51
+
52
+ use crate::raw::*;
53
+ use crate::*;
54
+
55
+ pub mod constants {
56
+ pub const X_AMZ_COPY_SOURCE: &str = "x-amz-copy-source";
57
+
58
+ pub const X_AMZ_SERVER_SIDE_ENCRYPTION: &str = "x-amz-server-side-encryption";
59
+ pub const X_AMZ_SERVER_REQUEST_PAYER: (&str, &str) = ("x-amz-request-payer", "requester");
60
+ pub const X_AMZ_SERVER_SIDE_ENCRYPTION_CUSTOMER_ALGORITHM: &str =
61
+ "x-amz-server-side-encryption-customer-algorithm";
62
+ pub const X_AMZ_SERVER_SIDE_ENCRYPTION_CUSTOMER_KEY: &str =
63
+ "x-amz-server-side-encryption-customer-key";
64
+ pub const X_AMZ_SERVER_SIDE_ENCRYPTION_CUSTOMER_KEY_MD5: &str =
65
+ "x-amz-server-side-encryption-customer-key-md5";
66
+ pub const X_AMZ_SERVER_SIDE_ENCRYPTION_AWS_KMS_KEY_ID: &str =
67
+ "x-amz-server-side-encryption-aws-kms-key-id";
68
+ pub const X_AMZ_STORAGE_CLASS: &str = "x-amz-storage-class";
69
+
70
+ pub const X_AMZ_COPY_SOURCE_SERVER_SIDE_ENCRYPTION_CUSTOMER_ALGORITHM: &str =
71
+ "x-amz-copy-source-server-side-encryption-customer-algorithm";
72
+ pub const X_AMZ_COPY_SOURCE_SERVER_SIDE_ENCRYPTION_CUSTOMER_KEY: &str =
73
+ "x-amz-copy-source-server-side-encryption-customer-key";
74
+ pub const X_AMZ_COPY_SOURCE_SERVER_SIDE_ENCRYPTION_CUSTOMER_KEY_MD5: &str =
75
+ "x-amz-copy-source-server-side-encryption-customer-key-md5";
76
+
77
+ pub const X_AMZ_WRITE_OFFSET_BYTES: &str = "x-amz-write-offset-bytes";
78
+
79
+ pub const X_AMZ_META_PREFIX: &str = "x-amz-meta-";
80
+
81
+ pub const X_AMZ_VERSION_ID: &str = "x-amz-version-id";
82
+ pub const X_AMZ_OBJECT_SIZE: &str = "x-amz-object-size";
83
+
84
+ pub const RESPONSE_CONTENT_DISPOSITION: &str = "response-content-disposition";
85
+ pub const RESPONSE_CONTENT_TYPE: &str = "response-content-type";
86
+ pub const RESPONSE_CACHE_CONTROL: &str = "response-cache-control";
87
+
88
+ pub const S3_QUERY_VERSION_ID: &str = "versionId";
89
+ }
90
+
91
+ pub struct S3Core {
92
+ pub info: Arc<AccessorInfo>,
93
+
94
+ pub bucket: String,
95
+ pub endpoint: String,
96
+ pub root: String,
97
+ pub server_side_encryption: Option<HeaderValue>,
98
+ pub server_side_encryption_aws_kms_key_id: Option<HeaderValue>,
99
+ pub server_side_encryption_customer_algorithm: Option<HeaderValue>,
100
+ pub server_side_encryption_customer_key: Option<HeaderValue>,
101
+ pub server_side_encryption_customer_key_md5: Option<HeaderValue>,
102
+ pub default_storage_class: Option<HeaderValue>,
103
+ pub allow_anonymous: bool,
104
+ pub disable_list_objects_v2: bool,
105
+ pub enable_request_payer: bool,
106
+
107
+ pub signer: AwsV4Signer,
108
+ pub loader: Box<dyn AwsCredentialLoad>,
109
+ pub credential_loaded: AtomicBool,
110
+ pub checksum_algorithm: Option<ChecksumAlgorithm>,
111
+ }
112
+
113
+ impl Debug for S3Core {
114
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
115
+ f.debug_struct("S3Core")
116
+ .field("bucket", &self.bucket)
117
+ .field("endpoint", &self.endpoint)
118
+ .field("root", &self.root)
119
+ .finish_non_exhaustive()
120
+ }
121
+ }
122
+
123
+ impl S3Core {
124
+ /// If credential is not found, we will not sign the request.
125
+ async fn load_credential(&self) -> Result<Option<AwsCredential>> {
126
+ let cred = self
127
+ .loader
128
+ .load_credential(GLOBAL_REQWEST_CLIENT.clone())
129
+ .await
130
+ .map_err(new_request_credential_error)?;
131
+
132
+ if let Some(cred) = cred {
133
+ // Update credential_loaded to true if we have load credential successfully.
134
+ self.credential_loaded
135
+ .store(true, atomic::Ordering::Relaxed);
136
+ return Ok(Some(cred));
137
+ }
138
+
139
+ // If we have load credential before but failed to load this time, we should
140
+ // return error instead.
141
+ if self.credential_loaded.load(atomic::Ordering::Relaxed) {
142
+ return Err(Error::new(
143
+ ErrorKind::PermissionDenied,
144
+ "credential was previously loaded successfully but has failed this time",
145
+ )
146
+ .set_temporary());
147
+ }
148
+
149
+ // Credential is empty and users allow anonymous access, we will not sign the request.
150
+ if self.allow_anonymous {
151
+ return Ok(None);
152
+ }
153
+
154
+ Err(Error::new(
155
+ ErrorKind::PermissionDenied,
156
+ "no valid credential found and anonymous access is not allowed",
157
+ ))
158
+ }
159
+
160
+ pub async fn sign<T>(&self, req: &mut Request<T>) -> Result<()> {
161
+ let cred = if let Some(cred) = self.load_credential().await? {
162
+ cred
163
+ } else {
164
+ return Ok(());
165
+ };
166
+
167
+ self.signer
168
+ .sign(req, &cred)
169
+ .map_err(new_request_sign_error)?;
170
+
171
+ // Always remove host header, let users' client to set it based on HTTP
172
+ // version.
173
+ //
174
+ // As discussed in <https://github.com/seanmonstar/reqwest/issues/1809>,
175
+ // google server could send RST_STREAM of PROTOCOL_ERROR if our request
176
+ // contains host header.
177
+ req.headers_mut().remove(HOST);
178
+
179
+ Ok(())
180
+ }
181
+
182
+ pub async fn sign_query<T>(&self, req: &mut Request<T>, duration: Duration) -> Result<()> {
183
+ let cred = if let Some(cred) = self.load_credential().await? {
184
+ cred
185
+ } else {
186
+ return Ok(());
187
+ };
188
+
189
+ self.signer
190
+ .sign_query(req, duration, &cred)
191
+ .map_err(new_request_sign_error)?;
192
+
193
+ // Always remove host header, let users' client to set it based on HTTP
194
+ // version.
195
+ //
196
+ // As discussed in <https://github.com/seanmonstar/reqwest/issues/1809>,
197
+ // google server could send RST_STREAM of PROTOCOL_ERROR if our request
198
+ // contains host header.
199
+ req.headers_mut().remove(HOST);
200
+
201
+ Ok(())
202
+ }
203
+
204
+ #[inline]
205
+ pub async fn send(&self, req: Request<Buffer>) -> Result<Response<Buffer>> {
206
+ self.info.http_client().send(req).await
207
+ }
208
+
209
+ /// # Note
210
+ ///
211
+ /// header like X_AMZ_SERVER_SIDE_ENCRYPTION doesn't need to set while
212
+ /// get or stat.
213
+ pub fn insert_sse_headers(
214
+ &self,
215
+ mut req: http::request::Builder,
216
+ is_write: bool,
217
+ ) -> http::request::Builder {
218
+ if is_write {
219
+ if let Some(v) = &self.server_side_encryption {
220
+ let mut v = v.clone();
221
+ v.set_sensitive(true);
222
+
223
+ req = req.header(
224
+ HeaderName::from_static(constants::X_AMZ_SERVER_SIDE_ENCRYPTION),
225
+ v,
226
+ )
227
+ }
228
+ if let Some(v) = &self.server_side_encryption_aws_kms_key_id {
229
+ let mut v = v.clone();
230
+ v.set_sensitive(true);
231
+
232
+ req = req.header(
233
+ HeaderName::from_static(constants::X_AMZ_SERVER_SIDE_ENCRYPTION_AWS_KMS_KEY_ID),
234
+ v,
235
+ )
236
+ }
237
+ }
238
+
239
+ if let Some(v) = &self.server_side_encryption_customer_algorithm {
240
+ let mut v = v.clone();
241
+ v.set_sensitive(true);
242
+
243
+ req = req.header(
244
+ HeaderName::from_static(constants::X_AMZ_SERVER_SIDE_ENCRYPTION_CUSTOMER_ALGORITHM),
245
+ v,
246
+ )
247
+ }
248
+ if let Some(v) = &self.server_side_encryption_customer_key {
249
+ let mut v = v.clone();
250
+ v.set_sensitive(true);
251
+
252
+ req = req.header(
253
+ HeaderName::from_static(constants::X_AMZ_SERVER_SIDE_ENCRYPTION_CUSTOMER_KEY),
254
+ v,
255
+ )
256
+ }
257
+ if let Some(v) = &self.server_side_encryption_customer_key_md5 {
258
+ let mut v = v.clone();
259
+ v.set_sensitive(true);
260
+
261
+ req = req.header(
262
+ HeaderName::from_static(constants::X_AMZ_SERVER_SIDE_ENCRYPTION_CUSTOMER_KEY_MD5),
263
+ v,
264
+ )
265
+ }
266
+
267
+ req
268
+ }
269
+ pub fn calculate_checksum(&self, body: &Buffer) -> Option<String> {
270
+ match self.checksum_algorithm {
271
+ None => None,
272
+ Some(ChecksumAlgorithm::Crc32c) => {
273
+ let mut crc = 0u32;
274
+ body.clone()
275
+ .for_each(|b| crc = crc32c::crc32c_append(crc, &b));
276
+ Some(BASE64_STANDARD.encode(crc.to_be_bytes()))
277
+ }
278
+ }
279
+ }
280
+ pub fn insert_checksum_header(
281
+ &self,
282
+ mut req: http::request::Builder,
283
+ checksum: &str,
284
+ ) -> http::request::Builder {
285
+ if let Some(checksum_algorithm) = self.checksum_algorithm.as_ref() {
286
+ req = req.header(checksum_algorithm.to_header_name(), checksum);
287
+ }
288
+ req
289
+ }
290
+
291
+ pub fn insert_checksum_type_header(
292
+ &self,
293
+ mut req: http::request::Builder,
294
+ ) -> http::request::Builder {
295
+ if let Some(checksum_algorithm) = self.checksum_algorithm.as_ref() {
296
+ req = req.header("x-amz-checksum-algorithm", checksum_algorithm.to_string());
297
+ }
298
+ req
299
+ }
300
+
301
+ pub fn insert_metadata_headers(
302
+ &self,
303
+ mut req: http::request::Builder,
304
+ size: Option<u64>,
305
+ args: &OpWrite,
306
+ ) -> http::request::Builder {
307
+ if let Some(size) = size {
308
+ req = req.header(CONTENT_LENGTH, size.to_string())
309
+ }
310
+
311
+ if let Some(mime) = args.content_type() {
312
+ req = req.header(CONTENT_TYPE, mime)
313
+ }
314
+
315
+ if let Some(pos) = args.content_disposition() {
316
+ req = req.header(CONTENT_DISPOSITION, pos)
317
+ }
318
+
319
+ if let Some(encoding) = args.content_encoding() {
320
+ req = req.header(CONTENT_ENCODING, encoding);
321
+ }
322
+
323
+ if let Some(cache_control) = args.cache_control() {
324
+ req = req.header(CACHE_CONTROL, cache_control)
325
+ }
326
+
327
+ if let Some(if_match) = args.if_match() {
328
+ req = req.header(IF_MATCH, if_match);
329
+ }
330
+
331
+ if args.if_not_exists() {
332
+ req = req.header(IF_NONE_MATCH, "*");
333
+ }
334
+
335
+ // Set storage class header
336
+ if let Some(v) = &self.default_storage_class {
337
+ req = req.header(HeaderName::from_static(constants::X_AMZ_STORAGE_CLASS), v);
338
+ }
339
+
340
+ // Set user metadata headers.
341
+ if let Some(user_metadata) = args.user_metadata() {
342
+ for (key, value) in user_metadata {
343
+ req = req.header(format!("{X_AMZ_META_PREFIX}{key}"), value)
344
+ }
345
+ }
346
+ req
347
+ }
348
+
349
+ pub fn insert_request_payer_header(
350
+ &self,
351
+ mut req: http::request::Builder,
352
+ ) -> http::request::Builder {
353
+ if self.enable_request_payer {
354
+ req = req.header(
355
+ HeaderName::from_static(constants::X_AMZ_SERVER_REQUEST_PAYER.0),
356
+ HeaderValue::from_static(constants::X_AMZ_SERVER_REQUEST_PAYER.1),
357
+ );
358
+ }
359
+ req
360
+ }
361
+ }
362
+
363
+ impl S3Core {
364
+ pub fn s3_head_object_request(&self, path: &str, args: OpStat) -> Result<Request<Buffer>> {
365
+ let p = build_abs_path(&self.root, path);
366
+
367
+ let mut url = format!("{}/{}", self.endpoint, percent_encode_path(&p));
368
+
369
+ // Add query arguments to the URL based on response overrides
370
+ let mut query_args = Vec::new();
371
+ if let Some(override_content_disposition) = args.override_content_disposition() {
372
+ query_args.push(format!(
373
+ "{}={}",
374
+ constants::RESPONSE_CONTENT_DISPOSITION,
375
+ percent_encode_path(override_content_disposition)
376
+ ))
377
+ }
378
+ if let Some(override_content_type) = args.override_content_type() {
379
+ query_args.push(format!(
380
+ "{}={}",
381
+ constants::RESPONSE_CONTENT_TYPE,
382
+ percent_encode_path(override_content_type)
383
+ ))
384
+ }
385
+ if let Some(override_cache_control) = args.override_cache_control() {
386
+ query_args.push(format!(
387
+ "{}={}",
388
+ constants::RESPONSE_CACHE_CONTROL,
389
+ percent_encode_path(override_cache_control)
390
+ ))
391
+ }
392
+ if let Some(version) = args.version() {
393
+ query_args.push(format!(
394
+ "{}={}",
395
+ constants::S3_QUERY_VERSION_ID,
396
+ percent_decode_path(version)
397
+ ))
398
+ }
399
+ if !query_args.is_empty() {
400
+ url.push_str(&format!("?{}", query_args.join("&")));
401
+ }
402
+
403
+ let mut req = Request::head(&url);
404
+
405
+ req = self.insert_sse_headers(req, false);
406
+
407
+ if let Some(if_none_match) = args.if_none_match() {
408
+ req = req.header(IF_NONE_MATCH, if_none_match);
409
+ }
410
+ if let Some(if_match) = args.if_match() {
411
+ req = req.header(IF_MATCH, if_match);
412
+ }
413
+
414
+ if let Some(if_modified_since) = args.if_modified_since() {
415
+ req = req.header(
416
+ IF_MODIFIED_SINCE,
417
+ format_datetime_into_http_date(if_modified_since),
418
+ );
419
+ }
420
+ if let Some(if_unmodified_since) = args.if_unmodified_since() {
421
+ req = req.header(
422
+ IF_UNMODIFIED_SINCE,
423
+ format_datetime_into_http_date(if_unmodified_since),
424
+ );
425
+ }
426
+
427
+ // Set request payer header if enabled.
428
+ req = self.insert_request_payer_header(req);
429
+
430
+ // Inject operation to the request.
431
+ req = req.extension(Operation::Stat);
432
+
433
+ let req = req.body(Buffer::new()).map_err(new_request_build_error)?;
434
+
435
+ Ok(req)
436
+ }
437
+
438
+ pub fn s3_get_object_request(
439
+ &self,
440
+ path: &str,
441
+ range: BytesRange,
442
+ args: &OpRead,
443
+ ) -> Result<Request<Buffer>> {
444
+ let p = build_abs_path(&self.root, path);
445
+
446
+ // Construct headers to add to the request
447
+ let mut url = format!("{}/{}", self.endpoint, percent_encode_path(&p));
448
+
449
+ // Add query arguments to the URL based on response overrides
450
+ let mut query_args = Vec::new();
451
+ if let Some(override_content_disposition) = args.override_content_disposition() {
452
+ query_args.push(format!(
453
+ "{}={}",
454
+ constants::RESPONSE_CONTENT_DISPOSITION,
455
+ percent_encode_path(override_content_disposition)
456
+ ))
457
+ }
458
+ if let Some(override_content_type) = args.override_content_type() {
459
+ query_args.push(format!(
460
+ "{}={}",
461
+ constants::RESPONSE_CONTENT_TYPE,
462
+ percent_encode_path(override_content_type)
463
+ ))
464
+ }
465
+ if let Some(override_cache_control) = args.override_cache_control() {
466
+ query_args.push(format!(
467
+ "{}={}",
468
+ constants::RESPONSE_CACHE_CONTROL,
469
+ percent_encode_path(override_cache_control)
470
+ ))
471
+ }
472
+ if let Some(version) = args.version() {
473
+ query_args.push(format!(
474
+ "{}={}",
475
+ constants::S3_QUERY_VERSION_ID,
476
+ percent_decode_path(version)
477
+ ))
478
+ }
479
+ if !query_args.is_empty() {
480
+ url.push_str(&format!("?{}", query_args.join("&")));
481
+ }
482
+
483
+ let mut req = Request::get(&url);
484
+
485
+ if !range.is_full() {
486
+ req = req.header(http::header::RANGE, range.to_header());
487
+ }
488
+
489
+ if let Some(if_none_match) = args.if_none_match() {
490
+ req = req.header(IF_NONE_MATCH, if_none_match);
491
+ }
492
+
493
+ if let Some(if_match) = args.if_match() {
494
+ req = req.header(IF_MATCH, if_match);
495
+ }
496
+
497
+ if let Some(if_modified_since) = args.if_modified_since() {
498
+ req = req.header(
499
+ IF_MODIFIED_SINCE,
500
+ format_datetime_into_http_date(if_modified_since),
501
+ );
502
+ }
503
+
504
+ if let Some(if_unmodified_since) = args.if_unmodified_since() {
505
+ req = req.header(
506
+ IF_UNMODIFIED_SINCE,
507
+ format_datetime_into_http_date(if_unmodified_since),
508
+ );
509
+ }
510
+
511
+ // Set request payer header if enabled.
512
+ req = self.insert_request_payer_header(req);
513
+
514
+ // Set SSE headers.
515
+ // TODO: how will this work with presign?
516
+ req = self.insert_sse_headers(req, false);
517
+
518
+ // Inject operation to the request.
519
+ req = req.extension(Operation::Read);
520
+
521
+ let req = req.body(Buffer::new()).map_err(new_request_build_error)?;
522
+
523
+ Ok(req)
524
+ }
525
+
526
+ pub async fn s3_get_object(
527
+ &self,
528
+ path: &str,
529
+ range: BytesRange,
530
+ args: &OpRead,
531
+ ) -> Result<Response<HttpBody>> {
532
+ let mut req = self.s3_get_object_request(path, range, args)?;
533
+
534
+ self.sign(&mut req).await?;
535
+
536
+ self.info.http_client().fetch(req).await
537
+ }
538
+
539
+ pub fn s3_put_object_request(
540
+ &self,
541
+ path: &str,
542
+ size: Option<u64>,
543
+ args: &OpWrite,
544
+ body: Buffer,
545
+ ) -> Result<Request<Buffer>> {
546
+ let p = build_abs_path(&self.root, path);
547
+
548
+ let url = format!("{}/{}", self.endpoint, percent_encode_path(&p));
549
+
550
+ let mut req = Request::put(&url);
551
+
552
+ req = self.insert_metadata_headers(req, size, args);
553
+
554
+ // Set request payer header if enabled.
555
+ req = self.insert_request_payer_header(req);
556
+
557
+ // Set SSE headers.
558
+ req = self.insert_sse_headers(req, true);
559
+
560
+ // Calculate Checksum.
561
+ if let Some(checksum) = self.calculate_checksum(&body) {
562
+ // Set Checksum header.
563
+ req = self.insert_checksum_header(req, &checksum);
564
+ }
565
+
566
+ // Inject operation to the request.
567
+ req = req.extension(Operation::Write);
568
+
569
+ // Set body
570
+ let req = req.body(body).map_err(new_request_build_error)?;
571
+
572
+ Ok(req)
573
+ }
574
+
575
+ pub fn s3_append_object_request(
576
+ &self,
577
+ path: &str,
578
+ position: u64,
579
+ size: u64,
580
+ args: &OpWrite,
581
+ body: Buffer,
582
+ ) -> Result<Request<Buffer>> {
583
+ let p = build_abs_path(&self.root, path);
584
+ let url = format!("{}/{}", self.endpoint, percent_encode_path(&p));
585
+ let mut req = Request::put(&url);
586
+
587
+ // Only include full metadata headers when creating a new object via append (position == 0)
588
+ // For existing objects or subsequent appends, only include content-length
589
+ if position == 0 {
590
+ req = self.insert_metadata_headers(req, Some(size), args);
591
+ } else {
592
+ req = req.header(CONTENT_LENGTH, size.to_string());
593
+ }
594
+
595
+ req = req.header(constants::X_AMZ_WRITE_OFFSET_BYTES, position.to_string());
596
+
597
+ // Set request payer header if enabled.
598
+ req = self.insert_request_payer_header(req);
599
+
600
+ // Set SSE headers.
601
+ req = self.insert_sse_headers(req, true);
602
+
603
+ // Inject operation to the request.
604
+ req = req.extension(Operation::Write);
605
+
606
+ // Set body
607
+ let req = req.body(body).map_err(new_request_build_error)?;
608
+
609
+ Ok(req)
610
+ }
611
+
612
+ pub async fn s3_head_object(&self, path: &str, args: OpStat) -> Result<Response<Buffer>> {
613
+ let mut req = self.s3_head_object_request(path, args)?;
614
+
615
+ self.sign(&mut req).await?;
616
+
617
+ self.send(req).await
618
+ }
619
+
620
+ pub async fn s3_delete_object(&self, path: &str, args: &OpDelete) -> Result<Response<Buffer>> {
621
+ let p = build_abs_path(&self.root, path);
622
+
623
+ let mut url = format!("{}/{}", self.endpoint, percent_encode_path(&p));
624
+
625
+ let mut query_args = Vec::new();
626
+
627
+ if let Some(version) = args.version() {
628
+ query_args.push(format!(
629
+ "{}={}",
630
+ constants::S3_QUERY_VERSION_ID,
631
+ percent_encode_path(version)
632
+ ))
633
+ }
634
+
635
+ if !query_args.is_empty() {
636
+ url.push_str(&format!("?{}", query_args.join("&")));
637
+ }
638
+
639
+ let mut req = Request::delete(&url);
640
+
641
+ // Set request payer header if enabled.
642
+ req = self.insert_request_payer_header(req);
643
+
644
+ let mut req = req
645
+ // Inject operation to the request.
646
+ .extension(Operation::Delete)
647
+ .body(Buffer::new())
648
+ .map_err(new_request_build_error)?;
649
+
650
+ self.sign(&mut req).await?;
651
+
652
+ self.send(req).await
653
+ }
654
+
655
+ pub async fn s3_copy_object(&self, from: &str, to: &str) -> Result<Response<Buffer>> {
656
+ let from = build_abs_path(&self.root, from);
657
+ let to = build_abs_path(&self.root, to);
658
+
659
+ let source = format!("{}/{}", self.bucket, percent_encode_path(&from));
660
+ let target = format!("{}/{}", self.endpoint, percent_encode_path(&to));
661
+
662
+ let mut req = Request::put(&target);
663
+
664
+ // Set SSE headers.
665
+ req = self.insert_sse_headers(req, true);
666
+
667
+ if let Some(v) = &self.server_side_encryption_customer_algorithm {
668
+ let mut v = v.clone();
669
+ v.set_sensitive(true);
670
+
671
+ req = req.header(
672
+ HeaderName::from_static(
673
+ constants::X_AMZ_COPY_SOURCE_SERVER_SIDE_ENCRYPTION_CUSTOMER_ALGORITHM,
674
+ ),
675
+ v,
676
+ )
677
+ }
678
+
679
+ if let Some(v) = &self.server_side_encryption_customer_key {
680
+ let mut v = v.clone();
681
+ v.set_sensitive(true);
682
+
683
+ req = req.header(
684
+ HeaderName::from_static(
685
+ constants::X_AMZ_COPY_SOURCE_SERVER_SIDE_ENCRYPTION_CUSTOMER_KEY,
686
+ ),
687
+ v,
688
+ )
689
+ }
690
+
691
+ if let Some(v) = &self.server_side_encryption_customer_key_md5 {
692
+ let mut v = v.clone();
693
+ v.set_sensitive(true);
694
+
695
+ req = req.header(
696
+ HeaderName::from_static(
697
+ constants::X_AMZ_COPY_SOURCE_SERVER_SIDE_ENCRYPTION_CUSTOMER_KEY_MD5,
698
+ ),
699
+ v,
700
+ )
701
+ }
702
+
703
+ // Set request payer header if enabled.
704
+ req = self.insert_request_payer_header(req);
705
+
706
+ let mut req = req
707
+ // Inject operation to the request.
708
+ .extension(Operation::Copy)
709
+ .header(constants::X_AMZ_COPY_SOURCE, &source)
710
+ .body(Buffer::new())
711
+ .map_err(new_request_build_error)?;
712
+
713
+ self.sign(&mut req).await?;
714
+
715
+ self.send(req).await
716
+ }
717
+
718
+ pub async fn s3_list_objects_v1(
719
+ &self,
720
+ path: &str,
721
+ marker: &str,
722
+ delimiter: &str,
723
+ limit: Option<usize>,
724
+ ) -> Result<Response<Buffer>> {
725
+ let p = build_abs_path(&self.root, path);
726
+
727
+ let mut url = QueryPairsWriter::new(&self.endpoint);
728
+
729
+ if !p.is_empty() {
730
+ url = url.push("prefix", &percent_encode_path(&p));
731
+ }
732
+ if !delimiter.is_empty() {
733
+ url = url.push("delimiter", delimiter);
734
+ }
735
+ if let Some(limit) = limit {
736
+ url = url.push("max-keys", &limit.to_string());
737
+ }
738
+ if !marker.is_empty() {
739
+ url = url.push("marker", &percent_encode_path(marker));
740
+ }
741
+
742
+ let mut req = Request::get(url.finish());
743
+
744
+ // Set request payer header if enabled.
745
+ req = self.insert_request_payer_header(req);
746
+
747
+ let mut req = req
748
+ // Inject operation to the request.
749
+ .extension(Operation::List)
750
+ .body(Buffer::new())
751
+ .map_err(new_request_build_error)?;
752
+
753
+ self.sign(&mut req).await?;
754
+
755
+ self.send(req).await
756
+ }
757
+
758
+ pub async fn s3_list_objects_v2(
759
+ &self,
760
+ path: &str,
761
+ continuation_token: &str,
762
+ delimiter: &str,
763
+ limit: Option<usize>,
764
+ start_after: Option<String>,
765
+ ) -> Result<Response<Buffer>> {
766
+ let p = build_abs_path(&self.root, path);
767
+
768
+ let mut url = QueryPairsWriter::new(&self.endpoint);
769
+ url = url.push("list-type", "2");
770
+
771
+ if !p.is_empty() {
772
+ url = url.push("prefix", &percent_encode_path(&p));
773
+ }
774
+ if !delimiter.is_empty() {
775
+ url = url.push("delimiter", delimiter);
776
+ }
777
+ if let Some(limit) = limit {
778
+ url = url.push("max-keys", &limit.to_string());
779
+ }
780
+ if let Some(start_after) = start_after {
781
+ url = url.push("start-after", &percent_encode_path(&start_after));
782
+ }
783
+ if !continuation_token.is_empty() {
784
+ // AWS S3 could return continuation-token that contains `=`
785
+ // which could lead `reqsign` parse query wrongly.
786
+ // URL encode continuation-token before starting signing so that
787
+ // our signer will not be confused.
788
+ url = url.push(
789
+ "continuation-token",
790
+ &percent_encode_path(continuation_token),
791
+ );
792
+ }
793
+
794
+ let mut req = Request::get(url.finish());
795
+
796
+ // Set request payer header if enabled.
797
+ req = self.insert_request_payer_header(req);
798
+
799
+ let mut req = req
800
+ // Inject operation to the request.
801
+ .extension(Operation::List)
802
+ .body(Buffer::new())
803
+ .map_err(new_request_build_error)?;
804
+
805
+ self.sign(&mut req).await?;
806
+
807
+ self.send(req).await
808
+ }
809
+
810
+ pub async fn s3_initiate_multipart_upload(
811
+ &self,
812
+ path: &str,
813
+ args: &OpWrite,
814
+ ) -> Result<Response<Buffer>> {
815
+ let p = build_abs_path(&self.root, path);
816
+
817
+ let url = format!("{}/{}?uploads", self.endpoint, percent_encode_path(&p));
818
+
819
+ let mut req = Request::post(&url);
820
+
821
+ if let Some(mime) = args.content_type() {
822
+ req = req.header(CONTENT_TYPE, mime)
823
+ }
824
+
825
+ if let Some(content_disposition) = args.content_disposition() {
826
+ req = req.header(CONTENT_DISPOSITION, content_disposition)
827
+ }
828
+
829
+ if let Some(cache_control) = args.cache_control() {
830
+ req = req.header(CACHE_CONTROL, cache_control)
831
+ }
832
+
833
+ // Set storage class header
834
+ if let Some(v) = &self.default_storage_class {
835
+ req = req.header(HeaderName::from_static(constants::X_AMZ_STORAGE_CLASS), v);
836
+ }
837
+
838
+ // Set user metadata headers.
839
+ if let Some(user_metadata) = args.user_metadata() {
840
+ for (key, value) in user_metadata {
841
+ req = req.header(format!("{X_AMZ_META_PREFIX}{key}"), value)
842
+ }
843
+ }
844
+
845
+ // Set request payer header if enabled.
846
+ req = self.insert_request_payer_header(req);
847
+
848
+ // Set SSE headers.
849
+ req = self.insert_sse_headers(req, true);
850
+
851
+ // Set SSE headers.
852
+ req = self.insert_checksum_type_header(req);
853
+
854
+ // Inject operation to the request.
855
+ req = req.extension(Operation::Write);
856
+
857
+ let mut req = req.body(Buffer::new()).map_err(new_request_build_error)?;
858
+
859
+ self.sign(&mut req).await?;
860
+
861
+ self.send(req).await
862
+ }
863
+
864
+ pub fn s3_upload_part_request(
865
+ &self,
866
+ path: &str,
867
+ upload_id: &str,
868
+ part_number: usize,
869
+ size: u64,
870
+ body: Buffer,
871
+ checksum: Option<String>,
872
+ ) -> Result<Request<Buffer>> {
873
+ let p = build_abs_path(&self.root, path);
874
+
875
+ let url = format!(
876
+ "{}/{}?partNumber={}&uploadId={}",
877
+ self.endpoint,
878
+ percent_encode_path(&p),
879
+ part_number,
880
+ percent_encode_path(upload_id)
881
+ );
882
+
883
+ let mut req = Request::put(&url);
884
+
885
+ req = req.header(CONTENT_LENGTH, size);
886
+
887
+ // Set request payer header if enabled.
888
+ req = self.insert_request_payer_header(req);
889
+
890
+ // Set SSE headers.
891
+ req = self.insert_sse_headers(req, true);
892
+
893
+ if let Some(checksum) = checksum {
894
+ // Set Checksum header.
895
+ req = self.insert_checksum_header(req, &checksum);
896
+ }
897
+
898
+ // Inject operation to the request.
899
+ req = req.extension(Operation::Write);
900
+
901
+ // Set body
902
+ let req = req.body(body).map_err(new_request_build_error)?;
903
+
904
+ Ok(req)
905
+ }
906
+
907
+ pub async fn s3_complete_multipart_upload(
908
+ &self,
909
+ path: &str,
910
+ upload_id: &str,
911
+ parts: Vec<CompleteMultipartUploadRequestPart>,
912
+ ) -> Result<Response<Buffer>> {
913
+ let p = build_abs_path(&self.root, path);
914
+
915
+ let url = format!(
916
+ "{}/{}?uploadId={}",
917
+ self.endpoint,
918
+ percent_encode_path(&p),
919
+ percent_encode_path(upload_id)
920
+ );
921
+
922
+ let mut req = Request::post(&url);
923
+
924
+ // Set SSE headers.
925
+ req = self.insert_sse_headers(req, true);
926
+
927
+ let content = quick_xml::se::to_string(&CompleteMultipartUploadRequest { part: parts })
928
+ .map_err(new_xml_serialize_error)?;
929
+ // Make sure content length has been set to avoid post with chunked encoding.
930
+ req = req.header(CONTENT_LENGTH, content.len());
931
+ // Set content-type to `application/xml` to avoid mixed with form post.
932
+ req = req.header(CONTENT_TYPE, "application/xml");
933
+
934
+ // Set request payer header if enabled.
935
+ req = self.insert_request_payer_header(req);
936
+
937
+ // Inject operation to the request.
938
+ req = req.extension(Operation::Write);
939
+
940
+ let mut req = req
941
+ .body(Buffer::from(Bytes::from(content)))
942
+ .map_err(new_request_build_error)?;
943
+
944
+ self.sign(&mut req).await?;
945
+
946
+ self.send(req).await
947
+ }
948
+
949
+ /// Abort an on-going multipart upload.
950
+ pub async fn s3_abort_multipart_upload(
951
+ &self,
952
+ path: &str,
953
+ upload_id: &str,
954
+ ) -> Result<Response<Buffer>> {
955
+ let p = build_abs_path(&self.root, path);
956
+
957
+ let url = format!(
958
+ "{}/{}?uploadId={}",
959
+ self.endpoint,
960
+ percent_encode_path(&p),
961
+ percent_encode_path(upload_id)
962
+ );
963
+
964
+ let mut req = Request::delete(&url);
965
+
966
+ // Set request payer header if enabled.
967
+ req = self.insert_request_payer_header(req);
968
+
969
+ let mut req = req
970
+ // Inject operation to the request.
971
+ .extension(Operation::Write)
972
+ .body(Buffer::new())
973
+ .map_err(new_request_build_error)?;
974
+
975
+ self.sign(&mut req).await?;
976
+ self.send(req).await
977
+ }
978
+
979
+ pub async fn s3_delete_objects(
980
+ &self,
981
+ paths: Vec<(String, OpDelete)>,
982
+ ) -> Result<Response<Buffer>> {
983
+ let url = format!("{}/?delete", self.endpoint);
984
+
985
+ let mut req = Request::post(&url);
986
+
987
+ let content = quick_xml::se::to_string(&DeleteObjectsRequest {
988
+ object: paths
989
+ .into_iter()
990
+ .map(|(path, op)| DeleteObjectsRequestObject {
991
+ key: build_abs_path(&self.root, &path),
992
+ version_id: op.version().map(|v| v.to_owned()),
993
+ })
994
+ .collect(),
995
+ })
996
+ .map_err(new_xml_serialize_error)?;
997
+
998
+ // Make sure content length has been set to avoid post with chunked encoding.
999
+ req = req.header(CONTENT_LENGTH, content.len());
1000
+ // Set content-type to `application/xml` to avoid mixed with form post.
1001
+ req = req.header(CONTENT_TYPE, "application/xml");
1002
+ // Set content-md5 as required by API.
1003
+ req = req.header("CONTENT-MD5", format_content_md5(content.as_bytes()));
1004
+
1005
+ // Set request payer header if enabled.
1006
+ req = self.insert_request_payer_header(req);
1007
+
1008
+ // Inject operation to the request.
1009
+ req = req.extension(Operation::Delete);
1010
+
1011
+ let mut req = req
1012
+ .body(Buffer::from(Bytes::from(content)))
1013
+ .map_err(new_request_build_error)?;
1014
+
1015
+ self.sign(&mut req).await?;
1016
+
1017
+ self.send(req).await
1018
+ }
1019
+
1020
+ pub async fn s3_list_object_versions(
1021
+ &self,
1022
+ prefix: &str,
1023
+ delimiter: &str,
1024
+ limit: Option<usize>,
1025
+ key_marker: &str,
1026
+ version_id_marker: &str,
1027
+ ) -> Result<Response<Buffer>> {
1028
+ let p = build_abs_path(&self.root, prefix);
1029
+
1030
+ let mut url = format!("{}?versions", self.endpoint);
1031
+ if !p.is_empty() {
1032
+ write!(url, "&prefix={}", percent_encode_path(p.as_str()))
1033
+ .expect("write into string must succeed");
1034
+ }
1035
+ if !delimiter.is_empty() {
1036
+ write!(url, "&delimiter={delimiter}").expect("write into string must succeed");
1037
+ }
1038
+
1039
+ if let Some(limit) = limit {
1040
+ write!(url, "&max-keys={limit}").expect("write into string must succeed");
1041
+ }
1042
+ if !key_marker.is_empty() {
1043
+ write!(url, "&key-marker={}", percent_encode_path(key_marker))
1044
+ .expect("write into string must succeed");
1045
+ }
1046
+ if !version_id_marker.is_empty() {
1047
+ write!(
1048
+ url,
1049
+ "&version-id-marker={}",
1050
+ percent_encode_path(version_id_marker)
1051
+ )
1052
+ .expect("write into string must succeed");
1053
+ }
1054
+
1055
+ let mut req = Request::get(&url);
1056
+
1057
+ // Set request payer header if enabled.
1058
+ req = self.insert_request_payer_header(req);
1059
+
1060
+ let mut req = req
1061
+ // Inject operation to the request.
1062
+ .extension(Operation::List)
1063
+ .body(Buffer::new())
1064
+ .map_err(new_request_build_error)?;
1065
+
1066
+ self.sign(&mut req).await?;
1067
+
1068
+ self.send(req).await
1069
+ }
1070
+ }
1071
+
1072
+ /// Result of CreateMultipartUpload
1073
+ #[derive(Default, Debug, Deserialize)]
1074
+ #[serde(default, rename_all = "PascalCase")]
1075
+ pub struct InitiateMultipartUploadResult {
1076
+ pub upload_id: String,
1077
+ }
1078
+
1079
+ /// Request of CompleteMultipartUploadRequest
1080
+ #[derive(Default, Debug, Serialize)]
1081
+ #[serde(default, rename = "CompleteMultipartUpload", rename_all = "PascalCase")]
1082
+ pub struct CompleteMultipartUploadRequest {
1083
+ pub part: Vec<CompleteMultipartUploadRequestPart>,
1084
+ }
1085
+
1086
+ #[derive(Clone, Default, Debug, Serialize)]
1087
+ #[serde(default, rename_all = "PascalCase")]
1088
+ pub struct CompleteMultipartUploadRequestPart {
1089
+ #[serde(rename = "PartNumber")]
1090
+ pub part_number: usize,
1091
+ /// # TODO
1092
+ ///
1093
+ /// quick-xml will do escape on `"` which leads to our serialized output is
1094
+ /// not the same as aws s3's example.
1095
+ ///
1096
+ /// Ideally, we could use `serialize_with` to address this (buf failed)
1097
+ ///
1098
+ /// ```ignore
1099
+ /// #[derive(Default, Debug, Serialize)]
1100
+ /// #[serde(default, rename_all = "PascalCase")]
1101
+ /// struct CompleteMultipartUploadRequestPart {
1102
+ /// #[serde(rename = "PartNumber")]
1103
+ /// part_number: usize,
1104
+ /// #[serde(rename = "ETag", serialize_with = "partial_escape")]
1105
+ /// etag: String,
1106
+ /// }
1107
+ ///
1108
+ /// fn partial_escape<S>(s: &str, ser: S) -> Result<S::Ok, S::Error>
1109
+ /// where
1110
+ /// S: serde::Serializer,
1111
+ /// {
1112
+ /// ser.serialize_str(&String::from_utf8_lossy(
1113
+ /// &quick_xml::escape::partial_escape(s.as_bytes()),
1114
+ /// ))
1115
+ /// }
1116
+ /// ```
1117
+ ///
1118
+ /// ref: <https://github.com/tafia/quick-xml/issues/362>
1119
+ #[serde(rename = "ETag")]
1120
+ pub etag: String,
1121
+ #[serde(rename = "ChecksumCRC32C", skip_serializing_if = "Option::is_none")]
1122
+ pub checksum_crc32c: Option<String>,
1123
+ }
1124
+
1125
+ /// Output of `CompleteMultipartUpload` operation
1126
+ #[derive(Debug, Default, Deserialize)]
1127
+ #[serde[default, rename_all = "PascalCase"]]
1128
+ pub struct CompleteMultipartUploadResult {
1129
+ pub bucket: String,
1130
+ pub key: String,
1131
+ pub location: String,
1132
+ #[serde(rename = "ETag")]
1133
+ pub etag: String,
1134
+ pub code: String,
1135
+ pub message: String,
1136
+ pub request_id: String,
1137
+ }
1138
+
1139
+ /// Request of DeleteObjects.
1140
+ #[derive(Default, Debug, Serialize)]
1141
+ #[serde(default, rename = "Delete", rename_all = "PascalCase")]
1142
+ pub struct DeleteObjectsRequest {
1143
+ pub object: Vec<DeleteObjectsRequestObject>,
1144
+ }
1145
+
1146
+ #[derive(Default, Debug, Serialize)]
1147
+ #[serde(rename_all = "PascalCase")]
1148
+ pub struct DeleteObjectsRequestObject {
1149
+ pub key: String,
1150
+ #[serde(skip_serializing_if = "Option::is_none")]
1151
+ pub version_id: Option<String>,
1152
+ }
1153
+
1154
+ /// Result of DeleteObjects.
1155
+ #[derive(Default, Debug, Deserialize)]
1156
+ #[serde(default, rename = "DeleteResult", rename_all = "PascalCase")]
1157
+ pub struct DeleteObjectsResult {
1158
+ pub deleted: Vec<DeleteObjectsResultDeleted>,
1159
+ pub error: Vec<DeleteObjectsResultError>,
1160
+ }
1161
+
1162
+ #[derive(Default, Debug, Deserialize)]
1163
+ #[serde(rename_all = "PascalCase")]
1164
+ pub struct DeleteObjectsResultDeleted {
1165
+ pub key: String,
1166
+ pub version_id: Option<String>,
1167
+ }
1168
+
1169
+ #[derive(Default, Debug, Deserialize)]
1170
+ #[serde(default, rename_all = "PascalCase")]
1171
+ pub struct DeleteObjectsResultError {
1172
+ pub code: String,
1173
+ pub key: String,
1174
+ pub message: String,
1175
+ pub version_id: Option<String>,
1176
+ }
1177
+
1178
+ /// Output of ListBucket/ListObjects (a.k.a ListObjectsV1).
1179
+ #[derive(Default, Debug, Deserialize)]
1180
+ #[serde(default, rename_all = "PascalCase")]
1181
+ pub struct ListObjectsOutputV1 {
1182
+ pub is_truncated: Option<bool>,
1183
+ /// ## Notes
1184
+ ///
1185
+ /// `next_marker` is returned only if we have the delimiter request parameter
1186
+ /// specified. If the response does not include the NextMarker element and it
1187
+ /// is truncated, we should use the value of the last Key element in the
1188
+ /// response as the marker parameter in the subsequent request to get the
1189
+ /// next set of object keys.
1190
+ ///
1191
+ /// If the contents is empty, we should find common_prefixes instead.
1192
+ pub next_marker: Option<String>,
1193
+ pub common_prefixes: Vec<OutputCommonPrefix>,
1194
+ pub contents: Vec<ListObjectsOutputContent>,
1195
+ }
1196
+
1197
+ /// Output of ListBucketV2/ListObjectsV2.
1198
+ ///
1199
+ /// ## Note
1200
+ ///
1201
+ /// Use `Option` in `is_truncated` and `next_continuation_token` to make
1202
+ /// the behavior more clear so that we can be compatible to more s3 services.
1203
+ ///
1204
+ /// And enable `serde(default)` so that we can keep going even when some field
1205
+ /// is not exist.
1206
+ #[derive(Default, Debug, Deserialize)]
1207
+ #[serde(default, rename_all = "PascalCase")]
1208
+ pub struct ListObjectsOutputV2 {
1209
+ pub is_truncated: Option<bool>,
1210
+ pub next_continuation_token: Option<String>,
1211
+ pub common_prefixes: Vec<OutputCommonPrefix>,
1212
+ pub contents: Vec<ListObjectsOutputContent>,
1213
+ }
1214
+
1215
+ #[derive(Default, Debug, Eq, PartialEq, Deserialize)]
1216
+ #[serde(rename_all = "PascalCase")]
1217
+ pub struct ListObjectsOutputContent {
1218
+ pub key: String,
1219
+ pub size: u64,
1220
+ pub last_modified: String,
1221
+ #[serde(rename = "ETag")]
1222
+ pub etag: Option<String>,
1223
+ }
1224
+
1225
+ #[derive(Default, Debug, Eq, PartialEq, Deserialize)]
1226
+ #[serde(rename_all = "PascalCase")]
1227
+ pub struct OutputCommonPrefix {
1228
+ pub prefix: String,
1229
+ }
1230
+
1231
+ /// Output of ListObjectVersions
1232
+ #[derive(Default, Debug, Deserialize)]
1233
+ #[serde(default, rename_all = "PascalCase")]
1234
+ pub struct ListObjectVersionsOutput {
1235
+ pub is_truncated: Option<bool>,
1236
+ pub next_key_marker: Option<String>,
1237
+ pub next_version_id_marker: Option<String>,
1238
+ pub common_prefixes: Vec<OutputCommonPrefix>,
1239
+ pub version: Vec<ListObjectVersionsOutputVersion>,
1240
+ pub delete_marker: Vec<ListObjectVersionsOutputDeleteMarker>,
1241
+ }
1242
+
1243
+ #[derive(Default, Debug, Eq, PartialEq, Deserialize)]
1244
+ #[serde(rename_all = "PascalCase")]
1245
+ pub struct ListObjectVersionsOutputVersion {
1246
+ pub key: String,
1247
+ pub version_id: String,
1248
+ pub is_latest: bool,
1249
+ pub size: u64,
1250
+ pub last_modified: String,
1251
+ #[serde(rename = "ETag")]
1252
+ pub etag: Option<String>,
1253
+ }
1254
+
1255
+ #[derive(Default, Debug, Eq, PartialEq, Deserialize)]
1256
+ #[serde(rename_all = "PascalCase")]
1257
+ pub struct ListObjectVersionsOutputDeleteMarker {
1258
+ pub key: String,
1259
+ pub version_id: String,
1260
+ pub is_latest: bool,
1261
+ pub last_modified: String,
1262
+ }
1263
+
1264
+ pub enum ChecksumAlgorithm {
1265
+ Crc32c,
1266
+ }
1267
+ impl ChecksumAlgorithm {
1268
+ pub fn to_header_name(&self) -> HeaderName {
1269
+ match self {
1270
+ Self::Crc32c => HeaderName::from_static("x-amz-checksum-crc32c"),
1271
+ }
1272
+ }
1273
+ }
1274
+ impl Display for ChecksumAlgorithm {
1275
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
1276
+ write!(
1277
+ f,
1278
+ "{}",
1279
+ match self {
1280
+ Self::Crc32c => "CRC32C",
1281
+ }
1282
+ )
1283
+ }
1284
+ }
1285
+
1286
+ #[cfg(test)]
1287
+ mod tests {
1288
+ use bytes::Buf;
1289
+ use bytes::Bytes;
1290
+
1291
+ use super::*;
1292
+
1293
+ /// This example is from https://docs.aws.amazon.com/AmazonS3/latest/API/API_CreateMultipartUpload.html#API_CreateMultipartUpload_Examples
1294
+ #[test]
1295
+ fn test_deserialize_initiate_multipart_upload_result() {
1296
+ let bs = Bytes::from(
1297
+ r#"<?xml version="1.0" encoding="UTF-8"?>
1298
+ <InitiateMultipartUploadResult xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
1299
+ <Bucket>example-bucket</Bucket>
1300
+ <Key>example-object</Key>
1301
+ <UploadId>VXBsb2FkIElEIGZvciA2aWWpbmcncyBteS1tb3ZpZS5tMnRzIHVwbG9hZA</UploadId>
1302
+ </InitiateMultipartUploadResult>"#,
1303
+ );
1304
+
1305
+ let out: InitiateMultipartUploadResult =
1306
+ quick_xml::de::from_reader(bs.reader()).expect("must success");
1307
+
1308
+ assert_eq!(
1309
+ out.upload_id,
1310
+ "VXBsb2FkIElEIGZvciA2aWWpbmcncyBteS1tb3ZpZS5tMnRzIHVwbG9hZA"
1311
+ )
1312
+ }
1313
+
1314
+ /// This example is from https://docs.aws.amazon.com/AmazonS3/latest/API/API_CompleteMultipartUpload.html#API_CompleteMultipartUpload_Examples
1315
+ #[test]
1316
+ fn test_serialize_complete_multipart_upload_request() {
1317
+ let req = CompleteMultipartUploadRequest {
1318
+ part: vec![
1319
+ CompleteMultipartUploadRequestPart {
1320
+ part_number: 1,
1321
+ etag: "\"a54357aff0632cce46d942af68356b38\"".to_string(),
1322
+ ..Default::default()
1323
+ },
1324
+ CompleteMultipartUploadRequestPart {
1325
+ part_number: 2,
1326
+ etag: "\"0c78aef83f66abc1fa1e8477f296d394\"".to_string(),
1327
+ ..Default::default()
1328
+ },
1329
+ CompleteMultipartUploadRequestPart {
1330
+ part_number: 3,
1331
+ etag: "\"acbd18db4cc2f85cedef654fccc4a4d8\"".to_string(),
1332
+ ..Default::default()
1333
+ },
1334
+ ],
1335
+ };
1336
+
1337
+ let actual = quick_xml::se::to_string(&req).expect("must succeed");
1338
+
1339
+ pretty_assertions::assert_eq!(
1340
+ actual,
1341
+ r#"<CompleteMultipartUpload>
1342
+ <Part>
1343
+ <PartNumber>1</PartNumber>
1344
+ <ETag>"a54357aff0632cce46d942af68356b38"</ETag>
1345
+ </Part>
1346
+ <Part>
1347
+ <PartNumber>2</PartNumber>
1348
+ <ETag>"0c78aef83f66abc1fa1e8477f296d394"</ETag>
1349
+ </Part>
1350
+ <Part>
1351
+ <PartNumber>3</PartNumber>
1352
+ <ETag>"acbd18db4cc2f85cedef654fccc4a4d8"</ETag>
1353
+ </Part>
1354
+ </CompleteMultipartUpload>"#
1355
+ // Cleanup space and new line
1356
+ .replace([' ', '\n'], "")
1357
+ )
1358
+ }
1359
+
1360
+ /// this example is from: https://docs.aws.amazon.com/AmazonS3/latest/API/API_CompleteMultipartUpload.html
1361
+ #[test]
1362
+ fn test_deserialize_complete_multipart_upload_result() {
1363
+ let bs = Bytes::from(
1364
+ r#"<?xml version="1.0" encoding="UTF-8"?>
1365
+ <CompleteMultipartUploadResult xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
1366
+ <Location>http://Example-Bucket.s3.region.amazonaws.com/Example-Object</Location>
1367
+ <Bucket>Example-Bucket</Bucket>
1368
+ <Key>Example-Object</Key>
1369
+ <ETag>"3858f62230ac3c915f300c664312c11f-9"</ETag>
1370
+ </CompleteMultipartUploadResult>"#,
1371
+ );
1372
+
1373
+ let out: CompleteMultipartUploadResult =
1374
+ quick_xml::de::from_reader(bs.reader()).expect("must success");
1375
+
1376
+ assert_eq!(out.bucket, "Example-Bucket");
1377
+ assert_eq!(out.key, "Example-Object");
1378
+ assert_eq!(
1379
+ out.location,
1380
+ "http://Example-Bucket.s3.region.amazonaws.com/Example-Object"
1381
+ );
1382
+ assert_eq!(out.etag, "\"3858f62230ac3c915f300c664312c11f-9\"");
1383
+ }
1384
+
1385
+ #[test]
1386
+ fn test_deserialize_complete_multipart_upload_result_when_return_error() {
1387
+ let bs = Bytes::from(
1388
+ r#"<?xml version="1.0" encoding="UTF-8"?>
1389
+
1390
+ <Error>
1391
+ <Code>InternalError</Code>
1392
+ <Message>We encountered an internal error. Please try again.</Message>
1393
+ <RequestId>656c76696e6727732072657175657374</RequestId>
1394
+ <HostId>Uuag1LuByRx9e6j5Onimru9pO4ZVKnJ2Qz7/C1NPcfTWAtRPfTaOFg==</HostId>
1395
+ </Error>"#,
1396
+ );
1397
+
1398
+ let out: CompleteMultipartUploadResult =
1399
+ quick_xml::de::from_reader(bs.reader()).expect("must success");
1400
+
1401
+ assert_eq!(out.code, "InternalError");
1402
+ assert_eq!(
1403
+ out.message,
1404
+ "We encountered an internal error. Please try again."
1405
+ );
1406
+ assert_eq!(out.request_id, "656c76696e6727732072657175657374");
1407
+ }
1408
+
1409
+ /// This example is from https://docs.aws.amazon.com/AmazonS3/latest/API/API_DeleteObjects.html#API_DeleteObjects_Examples
1410
+ #[test]
1411
+ fn test_serialize_delete_objects_request() {
1412
+ let req = DeleteObjectsRequest {
1413
+ object: vec![
1414
+ DeleteObjectsRequestObject {
1415
+ key: "sample1.txt".to_string(),
1416
+ version_id: None,
1417
+ },
1418
+ DeleteObjectsRequestObject {
1419
+ key: "sample2.txt".to_string(),
1420
+ version_id: Some("11111".to_owned()),
1421
+ },
1422
+ ],
1423
+ };
1424
+
1425
+ let actual = quick_xml::se::to_string(&req).expect("must succeed");
1426
+
1427
+ pretty_assertions::assert_eq!(
1428
+ actual,
1429
+ r#"<Delete>
1430
+ <Object>
1431
+ <Key>sample1.txt</Key>
1432
+ </Object>
1433
+ <Object>
1434
+ <Key>sample2.txt</Key>
1435
+ <VersionId>11111</VersionId>
1436
+ </Object>
1437
+ </Delete>"#
1438
+ // Cleanup space and new line
1439
+ .replace([' ', '\n'], "")
1440
+ )
1441
+ }
1442
+
1443
+ /// This example is from https://docs.aws.amazon.com/AmazonS3/latest/API/API_DeleteObjects.html#API_DeleteObjects_Examples
1444
+ #[test]
1445
+ fn test_deserialize_delete_objects_result() {
1446
+ let bs = Bytes::from(
1447
+ r#"<?xml version="1.0" encoding="UTF-8"?>
1448
+ <DeleteResult xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
1449
+ <Deleted>
1450
+ <Key>sample1.txt</Key>
1451
+ </Deleted>
1452
+ <Error>
1453
+ <Key>sample2.txt</Key>
1454
+ <Code>AccessDenied</Code>
1455
+ <Message>Access Denied</Message>
1456
+ </Error>
1457
+ </DeleteResult>"#,
1458
+ );
1459
+
1460
+ let out: DeleteObjectsResult =
1461
+ quick_xml::de::from_reader(bs.reader()).expect("must success");
1462
+
1463
+ assert_eq!(out.deleted.len(), 1);
1464
+ assert_eq!(out.deleted[0].key, "sample1.txt");
1465
+ assert_eq!(out.error.len(), 1);
1466
+ assert_eq!(out.error[0].key, "sample2.txt");
1467
+ assert_eq!(out.error[0].code, "AccessDenied");
1468
+ assert_eq!(out.error[0].message, "Access Denied");
1469
+ }
1470
+
1471
+ #[test]
1472
+ fn test_deserialize_delete_objects_with_version_id() {
1473
+ let bs = Bytes::from(
1474
+ r#"<?xml version="1.0" encoding="UTF-8"?>
1475
+ <DeleteResult xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
1476
+ <Deleted>
1477
+ <Key>SampleDocument.txt</Key>
1478
+ <VersionId>OYcLXagmS.WaD..oyH4KRguB95_YhLs7</VersionId>
1479
+ </Deleted>
1480
+ </DeleteResult>"#,
1481
+ );
1482
+
1483
+ let out: DeleteObjectsResult =
1484
+ quick_xml::de::from_reader(bs.reader()).expect("must success");
1485
+
1486
+ assert_eq!(out.deleted.len(), 1);
1487
+ assert_eq!(out.deleted[0].key, "SampleDocument.txt");
1488
+ assert_eq!(
1489
+ out.deleted[0].version_id,
1490
+ Some("OYcLXagmS.WaD..oyH4KRguB95_YhLs7".to_owned())
1491
+ );
1492
+ assert_eq!(out.error.len(), 0);
1493
+ }
1494
+
1495
+ /// This example is from https://docs.aws.amazon.com/AmazonS3/latest/API/API_ListObjects.html#API_ListObjects_Examples
1496
+ #[test]
1497
+ fn test_parse_list_output_v1() {
1498
+ let bs = bytes::Bytes::from(
1499
+ r#"<?xml version="1.0" encoding="UTF-8"?>
1500
+ <ListBucketResult xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
1501
+ <Name>bucket</Name>
1502
+ <Prefix/>
1503
+ <Marker/>
1504
+ <MaxKeys>1000</MaxKeys>
1505
+ <IsTruncated>false</IsTruncated>
1506
+ <Contents>
1507
+ <Key>my-image.jpg</Key>
1508
+ <LastModified>2009-10-12T17:50:30.000Z</LastModified>
1509
+ <ETag>"fba9dede5f27731c9771645a39863328"</ETag>
1510
+ <Size>434234</Size>
1511
+ <StorageClass>STANDARD</StorageClass>
1512
+ <Owner>
1513
+ <ID>75aa57f09aa0c8caeab4f8c24e99d10f8e7faeebf76c078efc7c6caea54ba06a</ID>
1514
+ <DisplayName>mtd@amazon.com</DisplayName>
1515
+ </Owner>
1516
+ </Contents>
1517
+ <Contents>
1518
+ <Key>my-third-image.jpg</Key>
1519
+ <LastModified>2009-10-12T17:50:30.000Z</LastModified>
1520
+ <ETag>"1b2cf535f27731c974343645a3985328"</ETag>
1521
+ <Size>64994</Size>
1522
+ <StorageClass>STANDARD_IA</StorageClass>
1523
+ <Owner>
1524
+ <ID>75aa57f09aa0c8caeab4f8c24e99d10f8e7faeebf76c078efc7c6caea54ba06a</ID>
1525
+ <DisplayName>mtd@amazon.com</DisplayName>
1526
+ </Owner>
1527
+ </Contents>
1528
+ </ListBucketResult>"#,
1529
+ );
1530
+
1531
+ let out: ListObjectsOutputV1 =
1532
+ quick_xml::de::from_reader(bs.reader()).expect("must success");
1533
+
1534
+ assert!(!out.is_truncated.unwrap());
1535
+ assert!(out.next_marker.is_none());
1536
+ assert!(out.common_prefixes.is_empty());
1537
+ assert_eq!(
1538
+ out.contents,
1539
+ vec![
1540
+ ListObjectsOutputContent {
1541
+ key: "my-image.jpg".to_string(),
1542
+ size: 434234,
1543
+ etag: Some("\"fba9dede5f27731c9771645a39863328\"".to_string()),
1544
+ last_modified: "2009-10-12T17:50:30.000Z".to_string(),
1545
+ },
1546
+ ListObjectsOutputContent {
1547
+ key: "my-third-image.jpg".to_string(),
1548
+ size: 64994,
1549
+ last_modified: "2009-10-12T17:50:30.000Z".to_string(),
1550
+ etag: Some("\"1b2cf535f27731c974343645a3985328\"".to_string()),
1551
+ },
1552
+ ]
1553
+ )
1554
+ }
1555
+
1556
+ #[test]
1557
+ fn test_parse_list_output_v2() {
1558
+ let bs = bytes::Bytes::from(
1559
+ r#"<ListBucketResult xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
1560
+ <Name>example-bucket</Name>
1561
+ <Prefix>photos/2006/</Prefix>
1562
+ <KeyCount>3</KeyCount>
1563
+ <MaxKeys>1000</MaxKeys>
1564
+ <Delimiter>/</Delimiter>
1565
+ <IsTruncated>false</IsTruncated>
1566
+ <Contents>
1567
+ <Key>photos/2006</Key>
1568
+ <LastModified>2016-04-30T23:51:29.000Z</LastModified>
1569
+ <ETag>"d41d8cd98f00b204e9800998ecf8427e"</ETag>
1570
+ <Size>56</Size>
1571
+ <StorageClass>STANDARD</StorageClass>
1572
+ </Contents>
1573
+ <Contents>
1574
+ <Key>photos/2007</Key>
1575
+ <LastModified>2016-04-30T23:51:29.000Z</LastModified>
1576
+ <ETag>"d41d8cd98f00b204e9800998ecf8427e"</ETag>
1577
+ <Size>100</Size>
1578
+ <StorageClass>STANDARD</StorageClass>
1579
+ </Contents>
1580
+ <Contents>
1581
+ <Key>photos/2008</Key>
1582
+ <LastModified>2016-05-30T23:51:29.000Z</LastModified>
1583
+ <Size>42</Size>
1584
+ </Contents>
1585
+
1586
+ <CommonPrefixes>
1587
+ <Prefix>photos/2006/February/</Prefix>
1588
+ </CommonPrefixes>
1589
+ <CommonPrefixes>
1590
+ <Prefix>photos/2006/January/</Prefix>
1591
+ </CommonPrefixes>
1592
+ </ListBucketResult>"#,
1593
+ );
1594
+
1595
+ let out: ListObjectsOutputV2 =
1596
+ quick_xml::de::from_reader(bs.reader()).expect("must success");
1597
+
1598
+ assert!(!out.is_truncated.unwrap());
1599
+ assert!(out.next_continuation_token.is_none());
1600
+ assert_eq!(
1601
+ out.common_prefixes
1602
+ .iter()
1603
+ .map(|v| v.prefix.clone())
1604
+ .collect::<Vec<String>>(),
1605
+ vec!["photos/2006/February/", "photos/2006/January/"]
1606
+ );
1607
+ assert_eq!(
1608
+ out.contents,
1609
+ vec![
1610
+ ListObjectsOutputContent {
1611
+ key: "photos/2006".to_string(),
1612
+ size: 56,
1613
+ etag: Some("\"d41d8cd98f00b204e9800998ecf8427e\"".to_string()),
1614
+ last_modified: "2016-04-30T23:51:29.000Z".to_string(),
1615
+ },
1616
+ ListObjectsOutputContent {
1617
+ key: "photos/2007".to_string(),
1618
+ size: 100,
1619
+ last_modified: "2016-04-30T23:51:29.000Z".to_string(),
1620
+ etag: Some("\"d41d8cd98f00b204e9800998ecf8427e\"".to_string()),
1621
+ },
1622
+ ListObjectsOutputContent {
1623
+ key: "photos/2008".to_string(),
1624
+ size: 42,
1625
+ last_modified: "2016-05-30T23:51:29.000Z".to_string(),
1626
+ etag: None,
1627
+ },
1628
+ ]
1629
+ )
1630
+ }
1631
+
1632
+ #[test]
1633
+ fn test_parse_list_object_versions() {
1634
+ let bs = bytes::Bytes::from(
1635
+ r#"<?xml version="1.0" encoding="UTF-8"?>
1636
+ <ListVersionsResult xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
1637
+ <Name>mtp-versioning-fresh</Name>
1638
+ <Prefix/>
1639
+ <KeyMarker>key3</KeyMarker>
1640
+ <VersionIdMarker>null</VersionIdMarker>
1641
+ <NextKeyMarker>key3</NextKeyMarker>
1642
+ <NextVersionIdMarker>d-d309mfjFrUmoQ0DBsVqmcMV15OI.</NextVersionIdMarker>
1643
+ <MaxKeys>3</MaxKeys>
1644
+ <IsTruncated>true</IsTruncated>
1645
+ <Version>
1646
+ <Key>key3</Key>
1647
+ <VersionId>8XECiENpj8pydEDJdd-_VRrvaGKAHOaGMNW7tg6UViI.</VersionId>
1648
+ <IsLatest>true</IsLatest>
1649
+ <LastModified>2009-12-09T00:18:23.000Z</LastModified>
1650
+ <ETag>"396fefef536d5ce46c7537ecf978a360"</ETag>
1651
+ <Size>217</Size>
1652
+ <Owner>
1653
+ <ID>75aa57f09aa0c8caeab4f8c24e99d10f8e7faeebf76c078efc7c6caea54ba06a</ID>
1654
+ </Owner>
1655
+ <StorageClass>STANDARD</StorageClass>
1656
+ </Version>
1657
+ <Version>
1658
+ <Key>key3</Key>
1659
+ <VersionId>d-d309mfjFri40QYukDozqBt3UmoQ0DBsVqmcMV15OI.</VersionId>
1660
+ <IsLatest>false</IsLatest>
1661
+ <LastModified>2009-12-09T00:18:08.000Z</LastModified>
1662
+ <ETag>"396fefef536d5ce46c7537ecf978a360"</ETag>
1663
+ <Size>217</Size>
1664
+ <Owner>
1665
+ <ID>75aa57f09aa0c8caeab4f8c24e99d10f8e7faeebf76c078efc7c6caea54ba06a</ID>
1666
+ </Owner>
1667
+ <StorageClass>STANDARD</StorageClass>
1668
+ </Version>
1669
+ <CommonPrefixes>
1670
+ <Prefix>photos/</Prefix>
1671
+ </CommonPrefixes>
1672
+ <CommonPrefixes>
1673
+ <Prefix>videos/</Prefix>
1674
+ </CommonPrefixes>
1675
+ <DeleteMarker>
1676
+ <Key>my-third-image.jpg</Key>
1677
+ <VersionId>03jpff543dhffds434rfdsFDN943fdsFkdmqnh892</VersionId>
1678
+ <IsLatest>true</IsLatest>
1679
+ <LastModified>2009-10-15T17:50:30.000Z</LastModified>
1680
+ <Owner>
1681
+ <ID>75aa57f09aa0c8caeab4f8c24e99d10f8e7faeebf76c078efc7c6caea54ba06a</ID>
1682
+ <DisplayName>mtd@amazon.com</DisplayName>
1683
+ </Owner>
1684
+ </DeleteMarker>
1685
+ </ListVersionsResult>"#,
1686
+ );
1687
+
1688
+ let output: ListObjectVersionsOutput =
1689
+ quick_xml::de::from_reader(bs.reader()).expect("must succeed");
1690
+
1691
+ assert!(output.is_truncated.unwrap());
1692
+ assert_eq!(output.next_key_marker, Some("key3".to_owned()));
1693
+ assert_eq!(
1694
+ output.next_version_id_marker,
1695
+ Some("d-d309mfjFrUmoQ0DBsVqmcMV15OI.".to_owned())
1696
+ );
1697
+ assert_eq!(
1698
+ output.common_prefixes,
1699
+ vec![
1700
+ OutputCommonPrefix {
1701
+ prefix: "photos/".to_owned()
1702
+ },
1703
+ OutputCommonPrefix {
1704
+ prefix: "videos/".to_owned()
1705
+ }
1706
+ ]
1707
+ );
1708
+
1709
+ assert_eq!(
1710
+ output.version,
1711
+ vec![
1712
+ ListObjectVersionsOutputVersion {
1713
+ key: "key3".to_owned(),
1714
+ version_id: "8XECiENpj8pydEDJdd-_VRrvaGKAHOaGMNW7tg6UViI.".to_owned(),
1715
+ is_latest: true,
1716
+ size: 217,
1717
+ last_modified: "2009-12-09T00:18:23.000Z".to_owned(),
1718
+ etag: Some("\"396fefef536d5ce46c7537ecf978a360\"".to_owned()),
1719
+ },
1720
+ ListObjectVersionsOutputVersion {
1721
+ key: "key3".to_owned(),
1722
+ version_id: "d-d309mfjFri40QYukDozqBt3UmoQ0DBsVqmcMV15OI.".to_owned(),
1723
+ is_latest: false,
1724
+ size: 217,
1725
+ last_modified: "2009-12-09T00:18:08.000Z".to_owned(),
1726
+ etag: Some("\"396fefef536d5ce46c7537ecf978a360\"".to_owned()),
1727
+ }
1728
+ ]
1729
+ );
1730
+
1731
+ assert_eq!(
1732
+ output.delete_marker,
1733
+ vec![ListObjectVersionsOutputDeleteMarker {
1734
+ key: "my-third-image.jpg".to_owned(),
1735
+ version_id: "03jpff543dhffds434rfdsFDN943fdsFkdmqnh892".to_owned(),
1736
+ is_latest: true,
1737
+ last_modified: "2009-10-15T17:50:30.000Z".to_owned(),
1738
+ },]
1739
+ );
1740
+ }
1741
+ }