pub mod error;
pub mod http;
pub mod io;
pub mod metadata;
pub use crate::error::{Result, RpmRepositoryError};
use {
crate::{
io::{read_decompressed, Compression, ContentDigest, ContentValidatingReader},
metadata::{
primary::Primary,
repomd::{RepoMd, RepoMdData},
},
},
futures::{AsyncRead, AsyncReadExt},
std::{future::Future, pin::Pin},
};
pub trait DataResolver: Sync {
#[allow(clippy::type_complexity)]
fn get_path(
&self,
path: String,
) -> Pin<Box<dyn Future<Output = Result<Pin<Box<dyn AsyncRead + Send>>>> + Send + '_>>;
#[allow(clippy::type_complexity)]
fn get_path_with_digest_verification(
&self,
path: String,
expected_size: u64,
expected_digest: ContentDigest,
) -> Pin<Box<dyn Future<Output = Result<Pin<Box<dyn AsyncRead + Send>>>> + Send + '_>> {
async fn run(
slf: &(impl DataResolver + ?Sized),
path: String,
expected_size: u64,
expected_digest: ContentDigest,
) -> Result<Pin<Box<dyn AsyncRead + Send>>> {
Ok(Box::pin(ContentValidatingReader::new(
slf.get_path(path).await?,
expected_size,
expected_digest,
)))
}
Box::pin(run(self, path, expected_size, expected_digest))
}
#[allow(clippy::type_complexity)]
fn get_path_decompressed(
&self,
path: String,
compression: Compression,
) -> Pin<Box<dyn Future<Output = Result<Pin<Box<dyn AsyncRead + Send>>>> + Send + '_>> {
async fn run(
slf: &(impl DataResolver + ?Sized),
path: String,
compression: Compression,
) -> Result<Pin<Box<dyn AsyncRead + Send>>> {
let reader = slf.get_path(path).await?;
Ok(read_decompressed(
Box::pin(futures::io::BufReader::new(reader)),
compression,
))
}
Box::pin(run(self, path, compression))
}
#[allow(clippy::type_complexity)]
fn get_path_decompressed_with_digest_verification(
&self,
path: String,
compression: Compression,
expected_size: u64,
expected_digest: ContentDigest,
) -> Pin<Box<dyn Future<Output = Result<Pin<Box<dyn AsyncRead + Send>>>> + Send + '_>> {
async fn run(
slf: &(impl DataResolver + ?Sized),
path: String,
compression: Compression,
expected_size: u64,
expected_digest: ContentDigest,
) -> Result<Pin<Box<dyn AsyncRead + Send>>> {
let reader = slf
.get_path_with_digest_verification(path, expected_size, expected_digest)
.await?;
Ok(read_decompressed(
Box::pin(futures::io::BufReader::new(reader)),
compression,
))
}
Box::pin(run(self, path, compression, expected_size, expected_digest))
}
}
pub trait RepositoryRootReader: DataResolver + Sync {
fn url(&self) -> Result<url::Url>;
#[allow(clippy::type_complexity)]
fn metadata_reader(
&self,
) -> Pin<Box<dyn Future<Output = Result<Box<dyn MetadataReader>>> + Send + '_>>;
fn fetch_repomd(
&self,
path: String,
) -> Pin<Box<dyn Future<Output = Result<RepoMd>> + Send + '_>> {
async fn run(slf: &(impl RepositoryRootReader + ?Sized), path: String) -> Result<RepoMd> {
let mut reader = slf.get_path(path.clone()).await?;
let mut data = vec![];
reader
.read_to_end(&mut data)
.await
.map_err(|e| RpmRepositoryError::IoPath(path, e))?;
RepoMd::from_reader(std::io::Cursor::new(data))
}
Box::pin(run(self, path))
}
}
pub trait MetadataReader: DataResolver + Sync {
fn url(&self) -> Result<url::Url>;
fn root_relative_path(&self) -> &str;
fn repomd(&self) -> &RepoMd;
#[allow(clippy::type_complexity)]
fn fetch_data_file<'slf>(
&'slf self,
data: &'slf RepoMdData,
) -> Pin<Box<dyn Future<Output = Result<Pin<Box<dyn AsyncRead + Send>>>> + Send + 'slf>> {
async fn run(
slf: &(impl MetadataReader + ?Sized),
data: &RepoMdData,
) -> Result<Pin<Box<dyn AsyncRead + Send>>> {
let path = data.location.href.as_str();
let expected_size = data.size.ok_or(RpmRepositoryError::MetadataMissingSize)?;
let expected_digest = ContentDigest::try_from(data.checksum.clone())?;
let compression = match path {
_ if path.ends_with(".gz") => Compression::Gzip,
_ if path.ends_with(".xz") => Compression::Xz,
_ => Compression::None,
};
slf.get_path_decompressed_with_digest_verification(
path.to_string(),
compression,
expected_size,
expected_digest,
)
.await
}
Box::pin(run(self, data))
}
#[allow(clippy::type_complexity)]
fn primary_packages(&self) -> Pin<Box<dyn Future<Output = Result<Primary>> + Send + '_>> {
async fn run(slf: &(impl MetadataReader + ?Sized)) -> Result<Primary> {
let primary = slf
.repomd()
.data
.iter()
.find(|entry| entry.data_type == "primary")
.ok_or(RpmRepositoryError::MetadataFileNotFound("primary"))?;
let mut reader = slf.fetch_data_file(primary).await?;
let mut data = vec![];
reader
.read_to_end(&mut data)
.await
.map_err(|e| RpmRepositoryError::IoPath(primary.location.href.clone(), e))?;
Primary::from_reader(std::io::Cursor::new(data))
}
Box::pin(run(self))
}
}