snix_castore/blob_engine/chunking/
writer.rs1use std::pin::pin;
2
3use pin_project_lite::pin_project;
4use tokio::io::AsyncWrite;
5use tonic::async_trait;
6use tracing::Span;
7
8use crate::{B3Digest, blobstore::BlobStore};
9
10pin_project! {
11pub(super) struct BlobWriterWrapper<BS>{
14 inner: Option<(crate::blobstore::BlobWriter, BS)>
15}
16}
17
18impl<BS> BlobWriterWrapper<BS> {
19 pub fn new(blob_writer: crate::blobstore::BlobWriter, blob_service: BS) -> Self {
20 Self {
21 inner: Some((blob_writer, blob_service)),
22 }
23 }
24}
25
26impl<BS> AsyncWrite for BlobWriterWrapper<BS>
27where
28 BS: BlobStore,
29{
30 fn poll_write(
31 self: std::pin::Pin<&mut Self>,
32 cx: &mut std::task::Context<'_>,
33 buf: &[u8],
34 ) -> std::task::Poll<std::io::Result<usize>> {
35 let (writer, _) = self.project().inner.as_mut().ok_or(std::io::Error::new(
36 std::io::ErrorKind::BrokenPipe,
37 "writer closed",
38 ))?;
39
40 pin!(writer).poll_write(cx, buf)
41 }
42
43 fn poll_flush(
44 self: std::pin::Pin<&mut Self>,
45 cx: &mut std::task::Context<'_>,
46 ) -> std::task::Poll<std::io::Result<()>> {
47 let (writer, _) = self.project().inner.as_mut().ok_or(std::io::Error::new(
48 std::io::ErrorKind::BrokenPipe,
49 "writer closed",
50 ))?;
51
52 pin!(writer).poll_flush(cx)
53 }
54
55 fn poll_shutdown(
56 self: std::pin::Pin<&mut Self>,
57 cx: &mut std::task::Context<'_>,
58 ) -> std::task::Poll<std::io::Result<()>> {
59 let (writer, _) = self.project().inner.as_mut().ok_or(std::io::Error::new(
60 std::io::ErrorKind::BrokenPipe,
61 "writer closed",
62 ))?;
63
64 pin!(writer).poll_shutdown(cx)
65 }
66}
67
68#[async_trait]
69impl<BS: BlobStore> crate::blobstore::Writer for BlobWriterWrapper<BS> {
70 #[tracing::instrument(skip_all, err, fields(blob.digest=tracing::field::Empty))]
71 async fn close(&mut self) -> std::io::Result<B3Digest> {
72 let (writer, blob_store) = self.inner.take().ok_or(std::io::Error::new(
73 std::io::ErrorKind::BrokenPipe,
74 "writer closed",
75 ))?;
76
77 let (blob_meta, blob_digest) = writer.close().await?;
78
79 Span::current().record("blob.digest", blob_digest.to_string());
80
81 if !blob_store
82 .has(&blob_digest)
83 .await
84 .map_err(std::io::Error::other)?
85 {
86 blob_store
87 .put(&blob_digest, blob_meta)
88 .await
89 .map_err(std::io::Error::other)?;
90 } else {
91 tracing::debug!("blob already exists, skipping upload")
92 }
93
94 return Ok(blob_digest);
95 }
96}