use {
crate::{
core::{
cache::{AsCacheRef, CacheRead, CacheWrite, DeserCache},
primitive::CommitLog,
storage,
workspace::{self, AsWorkspaceRef, Guard, Status, Workspace},
Bytes, Map,
},
Addr, Error, Path,
},
tokio::io,
};
pub struct Fixity<C, W> {
storage: C,
workspace: W,
}
impl<C, W> Fixity<C, W> {
pub fn new(storage: C, workspace: W) -> Self {
Self { storage, workspace }
}
pub fn into_cache(self) -> C {
self.into_cw().0
}
pub fn into_cw(self) -> (C, W) {
(self.storage, self.workspace)
}
}
impl Fixity<DeserCache<()>, workspace::Memory> {
pub fn memory() -> Self {
Self {
storage: DeserCache::new(()),
workspace: workspace::Memory::new("default".to_owned()),
}
}
}
impl<C, W> Fixity<C, W>
where
C: CacheRead + CacheWrite,
{
pub fn map(&self, path: Path) -> Map<'_, C, W> {
Map::new(&self.storage, &self.workspace, path)
}
pub fn bytes(&self, path: Path) -> Result<Bytes<'_, C, W>, Error> {
if path.is_empty() {
return Err(Error::CannotReplaceRootMap);
}
if !path.is_root_map() {
return Err(Error::CannotReplaceRootMap);
}
Ok(Bytes::new(&self.storage, &self.workspace, path))
}
}
#[derive(Debug, thiserror::Error)]
pub enum InitError {
#[error("failed creating fixity directory: `{source}`")]
CreateDir { source: io::Error },
#[error("failed creating new storage: `{source}`")]
Storage {
#[from]
source: storage::Error,
},
}
impl<C, W> AsWorkspaceRef for Fixity<C, W>
where
W: Workspace,
{
type Workspace = W;
fn as_workspace_ref(&self) -> &Self::Workspace {
&self.workspace
}
}
impl<C, W> AsCacheRef for Fixity<C, W>
where
C: CacheRead + CacheWrite,
{
type Cache = C;
fn as_cache_ref(&self) -> &Self::Cache {
&self.storage
}
}
#[async_trait::async_trait]
pub trait Commit {
async fn commit(&self) -> Result<Addr, Error>;
}
#[async_trait::async_trait]
impl<T> Commit for T
where
T: AsWorkspaceRef + AsCacheRef + Sync,
{
async fn commit(&self) -> Result<Addr, Error> {
let storage = self.as_cache_ref();
let workspace = self.as_workspace_ref();
let workspace_guard = workspace.lock().await?;
let (commit_addr, staged_content) = match workspace_guard.status().await? {
Status::InitStaged { staged_content, .. } => (None, staged_content),
Status::Staged {
commit,
staged_content,
..
} => (Some(commit), staged_content),
Status::Detached(_) => return Err(Error::DetachedHead),
Status::Init { .. } | Status::Clean { .. } => {
return Err(Error::NoStageToCommit);
},
};
let mut commit_log = CommitLog::new(storage, commit_addr);
let commit_addr = commit_log.append(staged_content).await?;
workspace_guard.commit(commit_addr.clone()).await?;
Ok(commit_addr)
}
}