use std::{
cell::{Ref, RefCell},
mem,
rc::Rc,
};
use hugr_core::{Hugr, Node};
use itertools::Itertools;
use relrc::Registry;
use crate::{Commit, InvalidCommit, PersistentHugr, PersistentReplacement};
pub mod serial;
pub type CommitId = relrc::NodeId;
#[derive(
Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, serde::Serialize, serde::Deserialize,
)]
pub struct PatchNode(pub CommitId, pub Node);
impl PatchNode {
pub fn owner(&self) -> CommitId {
self.0
}
}
impl std::fmt::Debug for PatchNode {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{:?}@{:?}", self.1, self.0)
}
}
impl std::fmt::Display for PatchNode {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{self:?}")
}
}
mod hidden {
use super::*;
#[derive(Debug, Clone, derive_more::From)]
pub enum CommitData {
Base(Hugr),
Replacement(PersistentReplacement),
}
}
pub(crate) use hidden::CommitData;
#[derive(Clone, Debug)]
#[repr(transparent)]
pub struct CommitStateSpace {
registry: Rc<RefCell<Registry<CommitData, ()>>>,
}
impl PartialEq for CommitStateSpace {
fn eq(&self, other: &Self) -> bool {
self.registry.as_ptr() == other.registry.as_ptr()
}
}
impl Eq for CommitStateSpace {}
impl From<Registry<CommitData, ()>> for CommitStateSpace {
fn from(registry: Registry<CommitData, ()>) -> Self {
Self {
registry: Rc::new(RefCell::new(registry)),
}
}
}
impl From<Rc<RefCell<Registry<CommitData, ()>>>> for CommitStateSpace {
fn from(registry: Rc<RefCell<Registry<CommitData, ()>>>) -> Self {
Self { registry }
}
}
impl<'a> From<&'a Rc<RefCell<Registry<CommitData, ()>>>> for &'a CommitStateSpace {
fn from(rc: &'a Rc<RefCell<Registry<CommitData, ()>>>) -> Self {
unsafe { mem::transmute(rc) }
}
}
impl Default for CommitStateSpace {
fn default() -> Self {
Self::new()
}
}
impl CommitStateSpace {
pub fn new() -> Self {
let registry = Rc::new(RefCell::new(Registry::new()));
Self { registry }
}
pub fn try_set_base(&self, hugr: Hugr) -> Option<Commit<'_>> {
if !self.registry.borrow().is_empty() {
return None;
}
Some(Commit::new_base(hugr, self))
}
pub fn contains(&self, commit: &Commit) -> bool {
self.borrow().contains(commit.as_relrc())
}
pub fn contains_id(&self, commit_id: CommitId) -> bool {
self.borrow().contains_id(commit_id)
}
pub fn get_id(&self, commit: &Commit) -> Option<CommitId> {
self.borrow().get_id(commit.as_relrc())
}
pub fn try_upgrade<'a>(&'a self, commit_id: CommitId) -> Option<Commit<'a>> {
self.borrow()
.get(commit_id)
.map(|rc| unsafe { Commit::from_relrc(rc) })
}
fn borrow(&self) -> Ref<'_, Registry<CommitData, ()>> {
self.registry.as_ref().borrow()
}
pub fn as_registry(&self) -> &Rc<RefCell<Registry<CommitData, ()>>> {
&self.registry
}
pub fn to_registry(&self) -> Rc<RefCell<Registry<CommitData, ()>>> {
self.registry.clone()
}
pub fn all_commits(&self) -> Vec<(CommitId, Commit<'_>)> {
self.borrow()
.iter()
.map(|(id, rc)| (id, unsafe { Commit::from_relrc(rc) }))
.collect()
}
pub fn try_create(
&self,
commits: impl IntoIterator<Item = CommitId>,
) -> Result<PersistentHugr, InvalidCommit> {
let commits: Vec<_> = commits
.into_iter()
.map(|id| {
self.try_upgrade(id)
.ok_or(InvalidCommit::UnknownCommitId(id))
})
.try_collect()?;
PersistentHugr::try_new(commits)
}
pub fn base_commit<'a>(&'a self) -> Option<Commit<'a>> {
let (_, relrc) = self.borrow().iter().next()?;
let commit: Commit<'a> = unsafe { Commit::from_relrc(relrc) };
Some(commit.base_commit().clone())
}
}