1use std::{collections::HashMap, convert::TryInto, ops::Deref};
2
3pub use builder::*;
4
5use js_sys::futures::JsFuture;
6use js_sys::{JsString, Reflect, Uint8Array};
7use wasm_bindgen::{JsCast, JsValue};
8use worker_sys::{
9 FixedLengthStream as EdgeFixedLengthStream, R2Bucket as EdgeR2Bucket, R2Checksums,
10 R2MultipartUpload as EdgeR2MultipartUpload, R2Object as EdgeR2Object,
11 R2ObjectBody as EdgeR2ObjectBody, R2Objects as EdgeR2Objects,
12 R2UploadedPart as EdgeR2UploadedPart,
13};
14
15use crate::{
16 env::EnvBinding, ByteStream, Date, Error, FixedLengthStream, Headers, ResponseBody, Result,
17};
18
19mod builder;
20
21#[derive(Debug, Clone)]
23pub struct Bucket {
24 inner: EdgeR2Bucket,
25}
26
27impl Bucket {
28 pub async fn head(&self, key: impl Into<String>) -> Result<Option<Object>> {
30 let head_promise = self.inner.head(key.into())?;
31 let value = JsFuture::from(head_promise).await?;
32
33 if value.is_null() {
34 return Ok(None);
35 }
36
37 Ok(Some(Object {
38 inner: ObjectInner::NoBody(value.into()),
39 }))
40 }
41
42 pub fn get(&self, key: impl Into<String>) -> GetOptionsBuilder<'_> {
46 GetOptionsBuilder {
47 edge_bucket: &self.inner,
48 key: key.into(),
49 only_if: None,
50 range: None,
51 }
52 }
53
54 pub fn put(&self, key: impl Into<String>, value: impl Into<Data>) -> PutOptionsBuilder<'_> {
60 PutOptionsBuilder {
61 edge_bucket: &self.inner,
62 key: key.into(),
63 value: value.into(),
64 http_metadata: None,
65 custom_metadata: None,
66 checksum: None,
67 checksum_algorithm: "md5".into(),
68 only_if: None,
69 }
70 }
71
72 pub async fn delete(&self, key: impl Into<String>) -> Result<()> {
78 let delete_promise = self.inner.delete(key.into())?;
79 JsFuture::from(delete_promise).await?;
80 Ok(())
81 }
82
83 pub async fn delete_multiple(&self, keys: Vec<impl Deref<Target = str>>) -> Result<()> {
92 let fut: JsFuture = self
93 .inner
94 .delete_multiple(keys.into_iter().map(|key| JsValue::from(&*key)).collect())?
95 .into();
96 fut.await?;
97 Ok(())
98 }
99
100 pub fn list(&self) -> ListOptionsBuilder<'_> {
103 ListOptionsBuilder {
104 edge_bucket: &self.inner,
105 limit: None,
106 prefix: None,
107 start_after: None,
108 cursor: None,
109 delimiter: None,
110 include: None,
111 }
112 }
113
114 pub fn create_multipart_upload(
120 &self,
121 key: impl Into<String>,
122 ) -> CreateMultipartUploadOptionsBuilder<'_> {
123 CreateMultipartUploadOptionsBuilder {
124 edge_bucket: &self.inner,
125 key: key.into(),
126 http_metadata: None,
127 custom_metadata: None,
128 }
129 }
130
131 pub fn resume_multipart_upload(
137 &self,
138 key: impl Into<String>,
139 upload_id: impl Into<String>,
140 ) -> Result<MultipartUpload> {
141 Ok(MultipartUpload {
142 inner: self
143 .inner
144 .resume_multipart_upload(key.into(), upload_id.into())?
145 .into(),
146 })
147 }
148}
149
150impl EnvBinding for Bucket {
151 const TYPE_NAME: &'static str = "R2Bucket";
152}
153
154impl JsCast for Bucket {
155 fn instanceof(val: &JsValue) -> bool {
156 val.is_instance_of::<EdgeR2Bucket>()
157 }
158
159 fn unchecked_from_js(val: JsValue) -> Self {
160 Self { inner: val.into() }
161 }
162
163 fn unchecked_from_js_ref(val: &JsValue) -> &Self {
164 unsafe { &*(val as *const JsValue as *const Self) }
165 }
166}
167
168impl From<Bucket> for JsValue {
169 fn from(bucket: Bucket) -> Self {
170 JsValue::from(bucket.inner)
171 }
172}
173
174impl AsRef<JsValue> for Bucket {
175 fn as_ref(&self) -> &JsValue {
176 &self.inner
177 }
178}
179
180#[derive(Debug)]
184pub struct Object {
185 inner: ObjectInner,
186}
187
188impl Object {
189 pub fn key(&self) -> String {
190 match &self.inner {
191 ObjectInner::NoBody(inner) => inner.key().unwrap(),
192 ObjectInner::Body(inner) => inner.key().unwrap(),
193 }
194 }
195
196 pub fn version(&self) -> String {
197 match &self.inner {
198 ObjectInner::NoBody(inner) => inner.version().unwrap(),
199 ObjectInner::Body(inner) => inner.version().unwrap(),
200 }
201 }
202
203 pub fn size(&self) -> u64 {
204 let size = match &self.inner {
205 ObjectInner::NoBody(inner) => inner.size().unwrap(),
206 ObjectInner::Body(inner) => inner.size().unwrap(),
207 };
208 size.round() as u64
209 }
210
211 pub fn etag(&self) -> String {
212 match &self.inner {
213 ObjectInner::NoBody(inner) => inner.etag().unwrap(),
214 ObjectInner::Body(inner) => inner.etag().unwrap(),
215 }
216 }
217
218 pub fn http_etag(&self) -> String {
219 match &self.inner {
220 ObjectInner::NoBody(inner) => inner.http_etag().unwrap(),
221 ObjectInner::Body(inner) => inner.http_etag().unwrap(),
222 }
223 }
224
225 pub fn uploaded(&self) -> Date {
226 match &self.inner {
227 ObjectInner::NoBody(inner) => inner.uploaded().unwrap(),
228 ObjectInner::Body(inner) => inner.uploaded().unwrap(),
229 }
230 .into()
231 }
232
233 pub fn http_metadata(&self) -> HttpMetadata {
234 match &self.inner {
235 ObjectInner::NoBody(inner) => inner.http_metadata().unwrap(),
236 ObjectInner::Body(inner) => inner.http_metadata().unwrap(),
237 }
238 .into()
239 }
240
241 pub fn checksum(&self) -> R2Checksums {
242 match &self.inner {
243 ObjectInner::NoBody(inner) => inner.checksums().unwrap(),
244 ObjectInner::Body(inner) => inner.checksums().unwrap(),
245 }
246 .into()
247 }
248
249 pub fn custom_metadata(&self) -> Result<HashMap<String, String>> {
250 let metadata = match &self.inner {
251 ObjectInner::NoBody(inner) => inner.custom_metadata().unwrap(),
252 ObjectInner::Body(inner) => inner.custom_metadata().unwrap(),
253 };
254
255 let keys = js_sys::Object::keys(&metadata).to_vec();
256 let mut map = HashMap::with_capacity(keys.len());
257
258 for key in keys {
259 let key = key.unchecked_into::<JsString>();
260 let value = Reflect::get(&metadata, &key)?.dyn_into::<JsString>()?;
261 map.insert(key.into(), value.into());
262 }
263
264 Ok(map)
265 }
266
267 pub fn range(&self) -> Result<Range> {
268 match &self.inner {
269 ObjectInner::NoBody(inner) => inner.range().unwrap(),
270 ObjectInner::Body(inner) => inner.range().unwrap(),
271 }
272 .try_into()
273 }
274
275 pub fn body(&self) -> Option<ObjectBody<'_>> {
276 match &self.inner {
277 ObjectInner::NoBody(_) => None,
278 ObjectInner::Body(body) => Some(ObjectBody { inner: body }),
279 }
280 }
281
282 pub fn body_used(&self) -> Option<bool> {
283 match &self.inner {
284 ObjectInner::NoBody(_) => None,
285 ObjectInner::Body(inner) => Some(inner.body_used().unwrap()),
286 }
287 }
288
289 pub fn write_http_metadata(&self, headers: Headers) -> Result<()> {
290 match &self.inner {
291 ObjectInner::NoBody(inner) => inner.write_http_metadata(headers.0)?,
292 ObjectInner::Body(inner) => inner.write_http_metadata(headers.0)?,
293 };
294
295 Ok(())
296 }
297}
298
299#[derive(Debug)]
301pub struct ObjectBody<'body> {
302 inner: &'body EdgeR2ObjectBody,
303}
304
305impl ObjectBody<'_> {
306 pub fn stream(self) -> Result<ByteStream> {
308 if self.inner.body_used()? {
309 return Err(Error::BodyUsed);
310 }
311
312 let stream = self.inner.body()?;
313 let stream = wasm_streams::ReadableStream::from_raw(stream.unchecked_into());
314 Ok(ByteStream {
315 inner: stream.into_stream(),
316 })
317 }
318
319 pub fn response_body(self) -> Result<ResponseBody> {
325 if self.inner.body_used()? {
326 return Err(Error::BodyUsed);
327 }
328
329 Ok(ResponseBody::Stream(self.inner.body()?))
330 }
331
332 pub async fn bytes(self) -> Result<Vec<u8>> {
333 let js_buffer = JsFuture::from(self.inner.array_buffer()?).await?;
334 let js_buffer = Uint8Array::new(&js_buffer);
335 let mut bytes = vec![0; js_buffer.length() as usize];
336 js_buffer.copy_to(&mut bytes);
337
338 Ok(bytes)
339 }
340
341 pub async fn text(self) -> Result<String> {
342 String::from_utf8(self.bytes().await?).map_err(|e| Error::RustError(e.to_string()))
343 }
344}
345
346#[derive(Debug)]
350pub struct UploadedPart {
351 inner: EdgeR2UploadedPart,
352}
353
354impl UploadedPart {
355 pub fn new(part_number: u16, etag: String) -> Self {
356 let obj = js_sys::Object::new();
357 Reflect::set(
358 &obj,
359 &JsValue::from_str("partNumber"),
360 &JsValue::from_f64(part_number as f64),
361 )
362 .unwrap();
363 Reflect::set(&obj, &JsValue::from_str("etag"), &JsValue::from_str(&etag)).unwrap();
364
365 let val: JsValue = obj.into();
366 Self { inner: val.into() }
367 }
368
369 pub fn part_number(&self) -> u16 {
370 self.inner.part_number().unwrap()
371 }
372
373 pub fn etag(&self) -> String {
374 self.inner.etag().unwrap()
375 }
376}
377
378#[derive(Debug)]
379pub struct MultipartUpload {
380 inner: EdgeR2MultipartUpload,
381}
382
383impl MultipartUpload {
384 pub async fn upload_part(
397 &self,
398 part_number: u16,
399 value: impl Into<Data>,
400 ) -> Result<UploadedPart> {
401 let uploaded_part =
402 JsFuture::from(self.inner.upload_part(part_number, value.into().into())?).await?;
403 Ok(UploadedPart {
404 inner: uploaded_part.into(),
405 })
406 }
407
408 pub async fn upload_id(&self) -> String {
410 self.inner.upload_id().unwrap()
411 }
412
413 pub async fn abort(&self) -> Result<()> {
415 JsFuture::from(self.inner.abort()?).await?;
416 Ok(())
417 }
418
419 pub async fn complete(
422 self,
423 uploaded_parts: impl IntoIterator<Item = UploadedPart>,
424 ) -> Result<Object> {
425 let object = JsFuture::from(
426 self.inner.complete(
427 uploaded_parts
428 .into_iter()
429 .map(|part| part.inner.into())
430 .collect(),
431 )?,
432 )
433 .await?;
434 Ok(Object {
435 inner: ObjectInner::Body(object.into()),
436 })
437 }
438}
439
440#[derive(Debug)]
442pub struct Objects {
443 inner: EdgeR2Objects,
444}
445
446impl Objects {
447 pub fn objects(&self) -> Vec<Object> {
449 self.inner
450 .objects()
451 .unwrap()
452 .into_iter()
453 .map(|raw| Object {
454 inner: ObjectInner::NoBody(raw),
455 })
456 .collect()
457 }
458
459 pub fn truncated(&self) -> bool {
462 self.inner.truncated().unwrap()
463 }
464
465 pub fn cursor(&self) -> Option<String> {
468 self.inner.cursor().unwrap()
469 }
470
471 pub fn delimited_prefixes(&self) -> Vec<String> {
478 self.inner
479 .delimited_prefixes()
480 .unwrap()
481 .into_iter()
482 .map(Into::into)
483 .collect()
484 }
485}
486
487#[derive(Debug, Clone)]
488pub(crate) enum ObjectInner {
489 NoBody(EdgeR2Object),
490 Body(EdgeR2ObjectBody),
491}
492
493#[derive(Debug)]
494pub enum Data {
495 ReadableStream(web_sys::ReadableStream),
496 Stream(FixedLengthStream),
497 Text(String),
498 Bytes(Vec<u8>),
499 Empty,
500}
501
502impl From<web_sys::ReadableStream> for Data {
503 fn from(stream: web_sys::ReadableStream) -> Self {
504 Data::ReadableStream(stream)
505 }
506}
507
508impl From<FixedLengthStream> for Data {
509 fn from(stream: FixedLengthStream) -> Self {
510 Data::Stream(stream)
511 }
512}
513
514impl From<String> for Data {
515 fn from(value: String) -> Self {
516 Data::Text(value)
517 }
518}
519
520impl From<Vec<u8>> for Data {
521 fn from(value: Vec<u8>) -> Self {
522 Data::Bytes(value)
523 }
524}
525
526impl From<Data> for JsValue {
527 fn from(data: Data) -> Self {
528 match data {
529 Data::ReadableStream(stream) => stream.into(),
530 Data::Stream(stream) => {
531 let stream_sys: EdgeFixedLengthStream = stream.into();
532 stream_sys.readable().into()
533 }
534 Data::Text(text) => JsString::from(text).into(),
535 Data::Bytes(bytes) => {
536 let arr = Uint8Array::new_with_length(bytes.len() as u32);
537 arr.copy_from(&bytes);
538 arr.into()
539 }
540 Data::Empty => JsValue::NULL,
541 }
542 }
543}