Skip to main content

bee/file/
data.rs

1//! `/bytes` endpoints: upload / download / probe.
2
3use bytes::Bytes;
4use reqwest::Method;
5use serde::Deserialize;
6
7use crate::api::{
8    DownloadOptions, RedundantUploadOptions, UploadResult, prepare_download_headers,
9    prepare_redundant_upload_headers,
10};
11use crate::client::{Inner, request};
12use crate::swarm::{BatchId, Error, Reference};
13
14use super::FileApi;
15
16/// Result of [`FileApi::probe_data`]: the size of the data behind a
17/// `/bytes` reference, learned via `HEAD` without downloading the body.
18#[derive(Clone, Debug, PartialEq, Eq)]
19pub struct ReferenceInformation {
20    /// `Content-Length` of the referenced data, in bytes.
21    pub content_length: u64,
22}
23
24#[derive(Deserialize)]
25struct UploadBody {
26    reference: String,
27}
28
29impl FileApi {
30    /// Upload raw bytes via `POST /bytes`. The body is sent as
31    /// `application/octet-stream`. Returns an [`UploadResult`] with the
32    /// content reference, optional tag UID, and (when ACT was
33    /// requested) the history address.
34    pub async fn upload_data(
35        &self,
36        batch_id: &BatchId,
37        data: impl Into<Bytes>,
38        opts: Option<&RedundantUploadOptions>,
39    ) -> Result<UploadResult, Error> {
40        let builder = request(&self.inner, Method::POST, "bytes")?
41            .header("Content-Type", "application/octet-stream")
42            .body(data.into());
43        let builder =
44            Inner::apply_headers(builder, prepare_redundant_upload_headers(batch_id, opts));
45        let resp = self.inner.send(builder).await?;
46        let headers = resp.headers().clone();
47        let body: UploadBody = serde_json::from_slice(&resp.bytes().await?)?;
48        UploadResult::from_response(&body.reference, &headers)
49    }
50
51    /// Download raw bytes via `GET /bytes/{ref}`. Returns the full body
52    /// in memory. For streaming downloads use
53    /// [`FileApi::download_data_response`](Self::download_data_response).
54    pub async fn download_data(
55        &self,
56        reference: &Reference,
57        opts: Option<&DownloadOptions>,
58    ) -> Result<Bytes, Error> {
59        let resp = self.download_data_response(reference, opts).await?;
60        Ok(resp.bytes().await?)
61    }
62
63    /// Download raw bytes via `GET /bytes/{ref}` and return the raw
64    /// [`reqwest::Response`] for streaming. The caller drives reading
65    /// from `resp.bytes_stream()` or `resp.chunk()`.
66    pub async fn download_data_response(
67        &self,
68        reference: &Reference,
69        opts: Option<&DownloadOptions>,
70    ) -> Result<reqwest::Response, Error> {
71        let path = format!("bytes/{}", reference.to_hex());
72        let builder = request(&self.inner, Method::GET, &path)?;
73        let builder = Inner::apply_headers(builder, prepare_download_headers(opts));
74        self.inner.send(builder).await
75    }
76
77    /// Probe the size of the data behind a `/bytes` reference using
78    /// a `HEAD` request. Mirrors bee-js `Bee.probeData`.
79    pub async fn probe_data(&self, reference: &Reference) -> Result<ReferenceInformation, Error> {
80        let path = format!("bytes/{}", reference.to_hex());
81        let builder = request(&self.inner, Method::HEAD, &path)?;
82        let resp = self.inner.send(builder).await?;
83        let content_length = resp
84            .headers()
85            .get(reqwest::header::CONTENT_LENGTH)
86            .and_then(|v| v.to_str().ok())
87            .and_then(|s| s.parse::<u64>().ok())
88            .or_else(|| resp.content_length())
89            .ok_or_else(|| Error::argument("missing Content-Length"))?;
90        Ok(ReferenceInformation { content_length })
91    }
92}