use std::sync::Arc;
use anyhow::{Result, bail};
use async_compression::tokio::bufread::{GzipDecoder, ZstdDecoder};
use containers_image_proxy::oci_spec::image::MediaType;
use tokio::io::{AsyncRead, AsyncWriteExt, BufReader};
use composefs::fsverity::FsVerityHashValue;
use composefs::repository::{ObjectStoreMethod, Repository};
use composefs::shared_internals::IO_BUF_CAPACITY;
use crate::skopeo::TAR_LAYER_CONTENT_TYPE;
use crate::tar::split_async;
pub fn is_tar_media_type(media_type: &MediaType) -> bool {
matches!(
media_type,
MediaType::ImageLayer
| MediaType::ImageLayerGzip
| MediaType::ImageLayerZstd
| MediaType::ImageLayerNonDistributable
| MediaType::ImageLayerNonDistributableGzip
| MediaType::ImageLayerNonDistributableZstd
)
}
pub fn decompress_async<'a, R>(
reader: R,
media_type: &MediaType,
) -> Result<Box<dyn AsyncRead + Unpin + Send + 'a>>
where
R: AsyncRead + Unpin + Send + 'a,
{
let buf = BufReader::new(reader);
let reader: Box<dyn AsyncRead + Unpin + Send> = match media_type {
MediaType::ImageLayer | MediaType::ImageLayerNonDistributable => {
Box::new(BufReader::with_capacity(IO_BUF_CAPACITY, buf))
}
MediaType::ImageLayerGzip | MediaType::ImageLayerNonDistributableGzip => Box::new(
BufReader::with_capacity(IO_BUF_CAPACITY, GzipDecoder::new(buf)),
),
MediaType::ImageLayerZstd | MediaType::ImageLayerNonDistributableZstd => Box::new(
BufReader::with_capacity(IO_BUF_CAPACITY, ZstdDecoder::new(buf)),
),
_ => bail!("Unsupported layer media type for decompression: {media_type}"),
};
Ok(reader)
}
pub async fn import_tar_async<ObjectID, R>(
repo: Arc<Repository<ObjectID>>,
reader: R,
) -> Result<(ObjectID, crate::ImportStats)>
where
ObjectID: FsVerityHashValue,
R: AsyncRead + Unpin + Send,
{
split_async(reader, repo, TAR_LAYER_CONTENT_TYPE).await
}
pub async fn store_blob_async<ObjectID, R>(
repo: &Repository<ObjectID>,
mut reader: R,
) -> Result<(ObjectID, u64, ObjectStoreMethod)>
where
ObjectID: FsVerityHashValue,
R: AsyncRead + Unpin,
{
let tmpfile = repo.create_object_tmpfile()?;
let mut writer = tokio::fs::File::from(std::fs::File::from(tmpfile));
let size = tokio::io::copy(&mut reader, &mut writer).await?;
writer.flush().await?;
let tmpfile = writer.into_std().await;
let (object_id, method) = repo.finalize_object_tmpfile(tmpfile, size)?;
Ok((object_id, size, method))
}