use std::ops::Deref;
use std::sync::Arc;
use crate::block::{Block, BlockChange, Primitive};
use crate::listen::{Gate, Listener, Notifier};
use crate::transaction::{
CommitError, Merge, PreconditionFailed, Transaction, TransactionConflict, Transactional,
};
use crate::universe::{RefError, RefVisitor, VisitRefs};
#[derive(Debug)]
pub struct BlockDef {
block: Block,
notifier: Arc<Notifier<BlockChange>>,
block_listen_gate: Gate,
}
impl BlockDef {
pub fn new(block: Block) -> Self {
let notifier = Arc::new(Notifier::new());
let (gate, block_listener) = Notifier::forwarder(Arc::downgrade(¬ifier)).gate();
let _ = block.listen(block_listener);
BlockDef {
block,
notifier,
block_listen_gate: gate,
}
}
pub fn listen(
&self,
listener: impl Listener<BlockChange> + Send + Sync + 'static,
) -> Result<(), RefError> {
self.notifier.listen(listener);
Ok(())
}
}
impl Deref for BlockDef {
type Target = Block;
fn deref(&self) -> &Block {
&self.block
}
}
impl AsRef<Block> for BlockDef {
fn as_ref(&self) -> &Block {
&self.block
}
}
impl VisitRefs for BlockDef {
fn visit_refs(&self, visitor: &mut dyn RefVisitor) {
self.block.visit_refs(visitor)
}
}
impl VisitRefs for Block {
fn visit_refs(&self, visitor: &mut dyn RefVisitor) {
self.primitive().visit_refs(visitor);
for modifier in self.modifiers() {
modifier.visit_refs(visitor)
}
}
}
impl VisitRefs for Primitive {
fn visit_refs(&self, visitor: &mut dyn RefVisitor) {
match self {
Primitive::Indirect(block_ref) => visitor.visit(block_ref),
Primitive::Atom(_, _) => {}
Primitive::Recur { space, .. } => visitor.visit(space),
}
}
}
impl Transactional for BlockDef {
type Transaction = BlockDefTransaction;
}
#[cfg(feature = "arbitrary")]
impl<'a> arbitrary::Arbitrary<'a> for BlockDef {
fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
Ok(BlockDef::new(Block::arbitrary(u)?))
}
fn size_hint(depth: usize) -> (usize, Option<usize>) {
Block::size_hint(depth)
}
}
#[derive(Clone, Debug, Default, Eq, Hash, PartialEq)]
#[must_use]
pub struct BlockDefTransaction {
old: Option<Block>,
new: Option<Block>,
}
impl BlockDefTransaction {
pub fn expect(old: Block) -> Self {
Self {
old: Some(old),
new: None,
}
}
pub fn overwrite(new: Block) -> Self {
Self {
old: None,
new: Some(new),
}
}
pub fn replace(old: Block, new: Block) -> Self {
Self {
old: Some(old),
new: Some(new),
}
}
}
impl Transaction<BlockDef> for BlockDefTransaction {
type CommitCheck = ();
type Output = ();
fn check(
&self,
target: &BlockDef,
) -> Result<Self::CommitCheck, crate::transaction::PreconditionFailed> {
if let Some(old) = &self.old {
if **target != *old {
return Err(PreconditionFailed {
location: "BlockDef",
problem: "existing block not as expected",
});
}
}
Ok(())
}
fn commit(
&self,
target: &mut BlockDef,
(): Self::CommitCheck,
) -> Result<Self::Output, CommitError> {
if let Some(new) = &self.new {
target.block = new.clone();
let (gate, block_listener) =
Notifier::forwarder(Arc::downgrade(&target.notifier)).gate();
let _ = target.block.listen(block_listener);
target.block_listen_gate = gate;
target.notifier.notify(BlockChange::new());
}
Ok(())
}
}
impl Merge for BlockDefTransaction {
type MergeCheck = ();
fn check_merge(
&self,
other: &Self,
) -> Result<Self::MergeCheck, crate::transaction::TransactionConflict> {
if matches!((&self.old, &other.old), (Some(a), Some(b)) if a != b) {
return Err(TransactionConflict {});
}
if matches!((&self.new, &other.new), (Some(a), Some(b)) if a != b) {
return Err(TransactionConflict {});
}
Ok(())
}
fn commit_merge(self, other: Self, (): Self::MergeCheck) -> Self
where
Self: Sized,
{
Self {
old: self.old.or(other.old),
new: self.new.or(other.new),
}
}
}