///|
/// Transactional callbacks owned by the host application. The pipeline never
/// chooses paths or opens files; a sink may stream bytes to disk, object
/// storage, a hash calculator, or an in-memory test collector.
pub struct UploadSink {
  on_begin : (UploadPart) -> Result[Unit, UploadError]
  on_data : (UploadPart, Bytes) -> Result[Unit, UploadError]
  on_end : (UploadPart) -> Result[Unit, UploadError]
  on_commit : (UploadReceipt) -> Result[Unit, UploadError]
  on_abort : (UploadError) -> Unit
}

///|
pub fn UploadSink::new(
  on_begin : (UploadPart) -> Result[Unit, UploadError],
  on_data : (UploadPart, Bytes) -> Result[Unit, UploadError],
  on_end : (UploadPart) -> Result[Unit, UploadError],
  on_commit : (UploadReceipt) -> Result[Unit, UploadError],
  on_abort : (UploadError) -> Unit,
) -> UploadSink {
  { on_begin, on_data, on_end, on_commit, on_abort }
}

///|
fn raise_sink(result : Result[Unit, UploadError]) -> Unit raise UploadError {
  match result {
    Ok(_) => ()
    Err(error) => raise error
  }
}