terrazzo-client 0.2.8

A simple macro to clone variables before passing them into a `move` closure or async block.
Documentation
use nameth::nameth;

use super::consumer_id::ConsumerId;
use super::producer::ProducedValue;
use super::producer::Producer;
use super::producer_weak::ProducerWeak;
use crate::debug_correlation_id::DebugCorrelationId;
use crate::prelude::OrElseLog as _;
use crate::prelude::diagnostics::trace;
use crate::string::XString;
use crate::utils::Ptr;
use crate::utils::PtrWeak;

#[must_use]
#[nameth]
pub struct Consumer<V: ProducedValue> {
    inner: Ptr<ConsumerInner<V, dyn Fn(V::Value)>>,
}

impl<V: ProducedValue> Consumer<V> {
    pub fn new<F>(
        consumer_name: DebugCorrelationId<XString>,
        producer: &Producer<V>,
        sort_key: V::SortKey,
        closure: F,
    ) -> Self
    where
        F: Fn(V::Value) + 'static,
    {
        let consumer_id = ConsumerId::new();
        trace!(%consumer_id, "New consumer");
        Self {
            inner: Ptr::new(ConsumerInner {
                id: consumer_id,
                name: consumer_name,
                sort_key,
                producer: producer.downgrade(),
                closure,
            }),
        }
    }

    pub fn consume(&self, value: V::Value) {
        (self.inner.closure)(value)
    }
}

impl<V: ProducedValue> Clone for Consumer<V> {
    fn clone(&self) -> Self {
        Self {
            inner: self.inner.clone(),
        }
    }
}

struct ConsumerInner<V: ProducedValue, F: Fn(V::Value) + ?Sized> {
    id: ConsumerId,
    name: DebugCorrelationId<XString>,
    sort_key: V::SortKey,
    producer: ProducerWeak<V>,
    closure: F,
}

impl<V: ProducedValue> Consumer<V> {
    pub(super) fn composite_key(&self) -> (&V::SortKey, ConsumerId) {
        (&self.inner.sort_key, self.inner.id)
    }
}

impl<V: ProducedValue, F: Fn(V::Value) + ?Sized> Drop for ConsumerInner<V, F> {
    fn drop(&mut self) {
        trace!(consumer_id = %self.id, consumer_name = %self.name, "Drop consumer");
        if let Some(producer) = self.producer.upgrade() {
            let mut producer_lock = producer.inner.1.lock().or_throw("producer_lock");
            let consumers = Ptr::try_unwrap(std::mem::take(&mut producer_lock.consumers))
                .unwrap_or_else(|consumers| consumers.as_ref().clone())
                .into_iter()
                .filter(|consumer| {
                    let Some(consumer) = consumer.upgrade() else {
                        return false;
                    };
                    return consumer.inner.id != self.id;
                })
                .collect();
            producer_lock.consumers = Ptr::new(consumers);
        }
    }
}

pub struct ConsumerWeak<V: ProducedValue> {
    inner: PtrWeak<ConsumerInner<V, dyn Fn(V::Value)>>,
}

impl<V: ProducedValue> Consumer<V> {
    pub(super) fn downgrade(&self) -> ConsumerWeak<V> {
        ConsumerWeak {
            inner: Ptr::downgrade(&self.inner),
        }
    }
}

impl<V: ProducedValue> ConsumerWeak<V> {
    pub(super) fn upgrade(&self) -> Option<Consumer<V>> {
        self.inner
            .upgrade()
            .map(|consumer| Consumer { inner: consumer })
    }
}

impl<V: ProducedValue> Clone for ConsumerWeak<V> {
    fn clone(&self) -> Self {
        Self {
            inner: self.inner.clone(),
        }
    }
}

impl<V: ProducedValue> std::fmt::Debug for Consumer<V> {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.debug_struct(CONSUMER)
            .field("id", &self.inner.id)
            .field("name", &self.inner.name)
            .finish()
    }
}