use {
super::{Error, Guard, Status, Workspace},
crate::Addr,
std::{collections::HashMap, mem, sync::Arc},
tokio::sync::{Mutex, MutexGuard},
};
#[derive(Debug, Clone)]
pub(super) enum HeadState {
Init {
branch: String,
},
InitStaged {
branch: String,
staged_content: Addr,
},
Clean {
branch: String,
},
Staged {
branch: String,
staged_content: Addr,
},
Aborted,
}
pub struct Memory {
guard: Mutex<()>,
state: Arc<Mutex<InnerMemory>>,
}
struct InnerMemory {
head: HeadState,
branches: HashMap<String, Addr>,
}
impl Memory {
pub fn new(_workspace: String) -> Self {
Self {
guard: Mutex::new(()),
state: Arc::new(Mutex::new(InnerMemory {
head: HeadState::Init {
branch: "default".to_owned(),
},
branches: HashMap::new(),
})),
}
}
}
#[async_trait::async_trait]
impl Workspace for Memory {
type Guard<'a> = MemoryGuard<'a>;
async fn lock(&self) -> Result<Self::Guard<'_>, Error> {
let _guard = self.guard.lock().await;
Ok(MemoryGuard {
_guard,
state: self.state.clone(),
})
}
async fn status(&self) -> Result<Status, Error> {
let inner = self.state.lock().await;
let status = match inner.head.clone() {
HeadState::Init { branch } => Status::Init { branch },
HeadState::InitStaged {
branch,
staged_content,
} => Status::InitStaged {
branch,
staged_content,
},
HeadState::Clean { branch } => {
let commit = inner
.branches
.get(&branch)
.ok_or_else(|| {
Error::Internal("HeadState::Clean but no internal address".to_owned())
})?
.clone();
Status::Clean { branch, commit }
},
HeadState::Staged {
branch,
staged_content,
} => {
let commit = inner
.branches
.get(&branch)
.ok_or_else(|| {
Error::Internal("HeadState::Clean but no internal address".to_owned())
})?
.clone();
Status::Staged {
branch,
commit,
staged_content,
}
},
HeadState::Aborted => return Err(Error::Internal("HeadState::Aborted".into())),
};
Ok(status)
}
}
#[allow(clippy::module_name_repetitions)]
pub struct MemoryGuard<'a> {
_guard: MutexGuard<'a, ()>,
state: Arc<Mutex<InnerMemory>>,
}
#[async_trait::async_trait]
impl<'a> Guard for MemoryGuard<'a> {
async fn stage(&self, staged_content: Addr) -> Result<(), Error> {
let mut inner = self.state.lock().await;
inner.head = match mem::replace(&mut inner.head, HeadState::Aborted) {
HeadState::Init { branch } | HeadState::InitStaged { branch, .. } => {
HeadState::InitStaged {
branch,
staged_content,
}
},
HeadState::Clean { branch } | HeadState::Staged { branch, .. } => HeadState::Staged {
branch,
staged_content,
},
HeadState::Aborted => return Err(Error::Internal("HeadState::Aborted".into())),
};
Ok(())
}
async fn commit(&self, commit_addr: Addr) -> Result<(), Error> {
let mut inner = self.state.lock().await;
if matches!(inner.head, HeadState::Init { .. } | HeadState::Clean { .. }) {
return Err(Error::CommitEmptyStage);
}
inner.head = match mem::replace(&mut inner.head, HeadState::Aborted) {
HeadState::InitStaged { branch, .. } | HeadState::Staged { branch, .. } => {
inner.branches.insert(branch.clone(), commit_addr);
HeadState::Clean { branch }
},
HeadState::Init { .. } | HeadState::Clean { .. } => {
return Err(Error::CommitEmptyStage);
},
HeadState::Aborted => return Err(Error::Internal("HeadState::Aborted".into())),
};
Ok(())
}
async fn status(&self) -> Result<Status, Error> {
let inner = self.state.lock().await;
let status = match inner.head.clone() {
HeadState::Init { branch } => Status::Init { branch },
HeadState::InitStaged {
branch,
staged_content,
} => Status::InitStaged {
branch,
staged_content,
},
HeadState::Clean { branch } => {
let commit = inner
.branches
.get(&branch)
.ok_or_else(|| {
Error::Internal("HeadState::Clean but no internal address".to_owned())
})?
.clone();
Status::Clean { branch, commit }
},
HeadState::Staged {
branch,
staged_content,
} => {
let commit = inner
.branches
.get(&branch)
.ok_or_else(|| {
Error::Internal("HeadState::Clean but no internal address".to_owned())
})?
.clone();
Status::Staged {
branch,
commit,
staged_content,
}
},
HeadState::Aborted => return Err(Error::Internal("HeadState::Aborted".into())),
};
Ok(status)
}
}