use harmos_domain::operations::storage::WriteFile;
use std::sync::Arc;
use crate::operations::{Context, Operation, OperationDeclaration, StreamInput};
use crate::storage::{FileReference, StorageManager};
use crate::{Error, Result};
use harmos_domain::model::storage::files::FileBatch;
pub(super) struct Handler<C> {
storage: Arc<StorageManager<C>>,
}
impl<C: 'static> Operation for Handler<C> {
type Declaration = WriteFile;
type Resources = StorageManager<C>;
type Error = Error;
type Context<'a> = Context<'a>;
type Endpoints = StreamInput<FileBatch>;
async fn initialize<'ctx>(storage: Arc<StorageManager<C>>, _: Self::Context<'ctx>) -> Result<Self> {
Ok(Self { storage })
}
async fn run<'a>(
&mut self, ctx: Self::Context<'a>, request: &mut WriteFile, input: &mut Self::Endpoints,
) -> Result<crate::operations::Response<FileReference>> {
let grant = harmos_domain::plugin::Grant::of(WriteFile::ID, request.metadata.id.as_str().to_owned());
ctx.admit_stream_grant(&grant)?;
self.storage.retry_transient_persistence();
let mut file = self.storage.create_file(request.metadata.clone()).await?;
let mut offset = 0u64;
while let Some(batch) = input.recv().await {
let (batch, _claims) = batch.into_owned_request()?;
ctx.admit_stream_grant(&grant)?;
if batch.offset != offset || batch.bytes.is_empty() {
return Err(Error::InvalidInput { field: "file batch", reason: "nonempty batches must have contiguous byte offsets" });
}
let next = offset
.checked_add(batch.bytes.len() as u64)
.ok_or(Error::InvalidInput { field: "file batch offset", reason: "byte offset overflow" })?;
file.write_all(batch.bytes).await?;
offset = next;
}
ctx.admit_stream_grant(&grant)?;
let reference = file.finish().await?;
self.storage.await_durable(&reference.position).await?;
Ok(reference.into())
}
fn classify(error: &Error) -> crate::operations::FailureKind {
super::classify(error)
}
}