use bytes::Bytes;
use reqwest::Method;
use serde::Deserialize;
use crate::api::{UploadOptions, UploadResult, prepare_upload_headers};
use crate::client::{Inner, request};
use crate::swarm::{
BatchId, Error, EthAddress, Identifier, PrivateKey, Reference, Signature, SingleOwnerChunk,
calculate_single_owner_chunk_address, make_single_owner_chunk, unmarshal_single_owner_chunk,
};
use super::FileApi;
use super::chunk::download_chunk_response;
#[derive(Deserialize)]
struct UploadBody {
reference: String,
}
impl FileApi {
pub async fn upload_soc(
&self,
batch_id: &BatchId,
owner: &EthAddress,
identifier: &Identifier,
signature: &Signature,
data: impl Into<Bytes>,
opts: Option<&UploadOptions>,
) -> Result<UploadResult, Error> {
let path = format!("soc/{}/{}", owner.to_hex(), identifier.to_hex());
let builder = request(&self.inner, Method::POST, &path)?
.header("Content-Type", "application/octet-stream")
.query(&[("sig", signature.to_hex())])
.body(data.into());
let builder = Inner::apply_headers(builder, prepare_upload_headers(batch_id, opts));
let resp = self.inner.send(builder).await?;
let headers = resp.headers().clone();
let body: UploadBody = serde_json::from_slice(&resp.bytes().await?)?;
UploadResult::from_response(&body.reference, &headers)
}
pub fn make_soc_reader(&self, owner: EthAddress) -> SocReader {
SocReader {
owner,
inner: self.inner.clone(),
}
}
pub fn make_soc_writer(&self, signer: PrivateKey) -> Result<SocWriter, Error> {
let owner = signer.public_key()?.address();
Ok(SocWriter {
reader: SocReader {
owner,
inner: self.inner.clone(),
},
signer,
})
}
}
#[derive(Clone, Debug)]
pub struct SocReader {
owner: EthAddress,
inner: std::sync::Arc<Inner>,
}
impl SocReader {
pub fn owner(&self) -> &EthAddress {
&self.owner
}
pub async fn download(&self, identifier: &Identifier) -> Result<SingleOwnerChunk, Error> {
let address = calculate_single_owner_chunk_address(identifier, &self.owner)?;
let resp = download_chunk_response(&self.inner, &address, None).await?;
let bytes = resp.bytes().await?;
unmarshal_single_owner_chunk(&bytes, &address)
}
}
#[derive(Clone, Debug)]
pub struct SocWriter {
reader: SocReader,
signer: PrivateKey,
}
impl SocWriter {
pub fn owner(&self) -> &EthAddress {
self.reader.owner()
}
pub fn reader(&self) -> &SocReader {
&self.reader
}
pub async fn upload(
&self,
batch_id: &BatchId,
identifier: &Identifier,
data: &[u8],
opts: Option<&UploadOptions>,
) -> Result<UploadResult, Error> {
let soc = make_single_owner_chunk(identifier, data, &self.signer)?;
let mut full = Vec::with_capacity(soc.span.as_bytes().len() + soc.payload.len());
full.extend_from_slice(soc.span.as_bytes());
full.extend_from_slice(&soc.payload);
let api = FileApi {
inner: self.reader.inner.clone(),
};
api.upload_soc(
batch_id,
&self.reader.owner,
identifier,
&soc.signature,
full,
opts,
)
.await
}
}
pub fn soc_address(identifier: &Identifier, owner: &EthAddress) -> Result<Reference, Error> {
calculate_single_owner_chunk_address(identifier, owner)
}