use std::io;
use async_trait::async_trait;
use gix_hash::ObjectId;
use crate::error::{RegistryError, StorageError};
use crate::limits::Limits;
use crate::registry::RepoRecord;
use crate::seam::{PreparedPush, PushOptions, RefLine, RefOutcome, RefUpdate};
#[async_trait]
pub trait GitRegistry: Send + Sync {
async fn resolve(&self, repo_id: &str) -> Result<RepoRecord, RegistryError>;
}
#[async_trait]
pub trait GitRegistryStore: GitRegistry {
async fn put_repo(&self, record: &RepoRecord) -> Result<(), RegistryError>;
async fn update_repo(&self, record: &RepoRecord) -> Result<(), RegistryError>;
async fn remove_repo(&self, repo_id: &str) -> Result<(), RegistryError>;
}
#[async_trait]
pub trait GitRefs: Send + Sync {
async fn list_refs(&self, repo: &RepoRecord) -> Result<Vec<RefLine>, StorageError>;
async fn apply_updates(
&self,
repo: &RepoRecord,
updates: Vec<RefUpdate>,
atomic: bool,
) -> Result<Vec<RefOutcome>, StorageError>;
}
#[async_trait]
pub trait GitPackGen: Send + Sync {
async fn generate(
&self,
repo: &RepoRecord,
wants: Vec<ObjectId>,
boundary_haves: Vec<ObjectId>,
limits: Limits,
sink: Box<dyn io::Write + Send>,
) -> Result<(), StorageError>;
async fn common_haves(
&self,
repo: &RepoRecord,
haves: Vec<ObjectId>,
) -> Result<Vec<ObjectId>, StorageError>;
}
#[async_trait]
pub trait GitPackIngest: Send + Sync {
async fn prepare(
&self,
repo: &RepoRecord,
updates: Vec<RefUpdate>,
pack: Box<dyn io::Read + Send>,
push_options: Option<PushOptions>,
limits: Limits,
) -> Result<PreparedPush, StorageError>;
}
#[cfg(test)]
mod tests {
use std::sync::Arc;
use super::*;
struct NoopRegistry;
struct NoopRegistryStore;
struct NoopRefs;
struct NoopPackGen;
struct NoopPackIngest;
#[async_trait]
impl GitRegistry for NoopRegistry {
async fn resolve(&self, _repo_id: &str) -> Result<RepoRecord, RegistryError> {
Err(RegistryError::NotFound)
}
}
#[async_trait]
impl GitRegistry for NoopRegistryStore {
async fn resolve(&self, _repo_id: &str) -> Result<RepoRecord, RegistryError> {
Err(RegistryError::NotFound)
}
}
#[async_trait]
impl GitRegistryStore for NoopRegistryStore {
async fn put_repo(&self, _record: &RepoRecord) -> Result<(), RegistryError> {
Ok(())
}
async fn update_repo(&self, _record: &RepoRecord) -> Result<(), RegistryError> {
Ok(())
}
async fn remove_repo(&self, _repo_id: &str) -> Result<(), RegistryError> {
Ok(())
}
}
#[async_trait]
impl GitRefs for NoopRefs {
async fn list_refs(&self, _repo: &RepoRecord) -> Result<Vec<RefLine>, StorageError> {
Ok(Vec::new())
}
async fn apply_updates(
&self,
_repo: &RepoRecord,
_updates: Vec<RefUpdate>,
_atomic: bool,
) -> Result<Vec<RefOutcome>, StorageError> {
Ok(Vec::new())
}
}
#[async_trait]
impl GitPackGen for NoopPackGen {
async fn generate(
&self,
_repo: &RepoRecord,
_wants: Vec<ObjectId>,
_boundary_haves: Vec<ObjectId>,
_limits: Limits,
_sink: Box<dyn io::Write + Send>,
) -> Result<(), StorageError> {
Ok(())
}
async fn common_haves(
&self,
_repo: &RepoRecord,
_haves: Vec<ObjectId>,
) -> Result<Vec<ObjectId>, StorageError> {
Ok(Vec::new())
}
}
#[async_trait]
impl GitPackIngest for NoopPackIngest {
async fn prepare(
&self,
_repo: &RepoRecord,
_updates: Vec<RefUpdate>,
_pack: Box<dyn io::Read + Send>,
_push_options: Option<PushOptions>,
_limits: Limits,
) -> Result<PreparedPush, StorageError> {
Ok(PreparedPush {
unpack: Ok(()),
updates: Vec::new(),
})
}
}
#[tokio::test]
async fn all_five_traits_are_dyn_compatible() {
let registry: Arc<dyn GitRegistry> = Arc::new(NoopRegistry);
let store: Arc<dyn GitRegistryStore> = Arc::new(NoopRegistryStore);
let refs: Arc<dyn GitRefs> = Arc::new(NoopRefs);
let gen: Arc<dyn GitPackGen> = Arc::new(NoopPackGen);
let ingest: Arc<dyn GitPackIngest> = Arc::new(NoopPackIngest);
let repo = RepoRecord {
repo_id: "demo".to_string(),
visibility: crate::registry::Visibility::Private,
grants: std::collections::BTreeMap::new(),
};
assert_eq!(
registry.resolve("demo").await.unwrap_err(),
RegistryError::NotFound
);
assert!(store.put_repo(&repo).await.is_ok());
assert!(store.update_repo(&repo).await.is_ok());
assert!(store.remove_repo("demo").await.is_ok());
assert!(refs.list_refs(&repo).await.unwrap().is_empty());
let outcomes = refs.apply_updates(&repo, Vec::new(), true).await.unwrap();
assert!(outcomes.is_empty());
gen.generate(
&repo,
Vec::new(),
Vec::new(),
Limits::default(),
Box::new(io::sink()),
)
.await
.unwrap();
assert!(gen
.common_haves(&repo, Vec::new())
.await
.unwrap()
.is_empty());
let prepared = ingest
.prepare(
&repo,
Vec::new(),
Box::new(io::empty()),
None,
Limits::default(),
)
.await
.unwrap();
assert!(prepared.unpack.is_ok());
}
}