mutcy 0.5.2

Zero-cost safe mutable cyclic borrows using borrow relinquishing
Documentation
use super::{FractionalIndexType, Handler, Receiver, Signal};
use crate::{Key, KeyCell, Meta, Rw};
use std::{
    collections::HashMap,
    rc::{Rc, Weak},
};

pub enum Action {
    Continue,
    Remove,
    RemoveSignal(FractionalIndexType),
}

pub struct SignalInner<T> {
    receivers: Vec<Receiver<T>>,
    index: usize,
    depth: usize,
    top: usize,
    idgen: u64,
    subsignals: HashMap<FractionalIndexType, Signal<T>>,
}

impl<T: 'static> SignalInner<T> {
    pub fn new() -> Self {
        Self {
            receivers: Vec::new(),
            index: 0,
            depth: 0,
            top: 0,
            idgen: 0,
            subsignals: HashMap::new(),
        }
    }

    pub fn connect<R, F>(
        self: Rw<Self>,
        receiver: Weak<KeyCell<R>>,
        order: FractionalIndexType,
        handler: F,
    ) -> u64
    where
        R: Meta + 'static,
        F: Fn(Rw<R>, &T) + 'static,
    {
        debug_assert!(order % 2 == 0 && order != 0);
        assert!(
            Weak::strong_count(&receiver) > 0,
            "Signal::connect: object must have a strong reference"
        );

        let handler: Rc<Handler<T>> = Rc::new(move |key, item| {
            if let Some(receiver) = receiver.upgrade() {
                (handler)(&mut receiver.rw(key), item);
                Action::Continue
            } else {
                Action::Remove
            }
        });

        let id = self.idgen;
        self.idgen += 1;

        let idx = self
            .receivers
            .partition_point(|x| x.relative_index <= order);

        self.receivers.insert(
            idx,
            Receiver {
                handler,
                relative_index: order,
                id,
            },
        );

        if idx < self.index {
            self.index -= 1;
        }

        id
    }

    pub fn subsignal(self: Rw<Self>, order: FractionalIndexType) -> Rc<KeyCell<Self>> {
        let signal = Rc::new(KeyCell::new(Self::new(), ()));

        let receiver = signal.clone();

        let handler: Rc<Handler<T>> = Rc::new(move |key, item| {
            let strong = Rc::strong_count(&receiver);
            let mut receiver = receiver.rw(key);

            if strong == 1 && receiver.len() == 0 {
                return Action::Remove;
            }

            if strong == 2 && receiver.len() == 0 && order % 2 == 1 {
                return Action::RemoveSignal(order);
            }

            receiver.emit(item);
            Action::Continue
        });

        let id = self.idgen;
        self.idgen += 1;

        let idx = self
            .receivers
            .partition_point(|x| x.relative_index <= order);

        self.receivers.insert(
            idx,
            Receiver {
                handler,
                relative_index: order,
                id,
            },
        );

        if idx < self.index {
            self.index -= 1;
        }

        signal
    }

    pub fn emit(self: Rw<Self>, item: &T) {
        self.top = self.depth;
        let top = self.top;
        self.depth += 1;
        self.index = 0;

        struct DeferDec<'a, 'b, T>(Rw<'a, 'b, SignalInner<T>>);

        impl<T> Drop for DeferDec<'_, '_, T> {
            fn drop(&mut self) {
                self.0.depth -= 1;
            }
        }

        let this = DeferDec(self);

        while let Some(receiver) = this.0.receivers.get(this.0.index) {
            let receiver = receiver.handler.clone();
            this.0.index += 1;

            let (_, key) = Key::split_rw(this.0);

            match (receiver)(key, item) {
                Action::Remove => {
                    this.0.index -= 1;
                    let index = this.0.index;
                    this.0.receivers.remove(index);
                }
                Action::Continue => {}
                Action::RemoveSignal(position) => {
                    this.0.index -= 1;
                    let index = this.0.index;
                    this.0.receivers.remove(index);
                    this.0.subsignals.remove(&position);
                }
            }

            if this.0.top > top {
                break;
            }
        }

        drop(this);
    }

    pub fn disconnect(self: Rw<Self>, id: u64, relative_index: FractionalIndexType) {
        let left = self
            .receivers
            .partition_point(|x| x.relative_index < relative_index);
        let right = self
            .receivers
            .partition_point(|x| x.relative_index <= relative_index);

        let idx = self.receivers[left..right].partition_point(|x| x.id < id);

        if idx + left >= self.receivers.len() {
            return;
        }

        if self.receivers[idx + left].id != id {
            return;
        }

        self.receivers.remove(idx + left);

        if idx + left < self.index {
            self.index -= 1;
        }
    }

    pub fn len(&self) -> usize {
        self.receivers.len()
    }

    pub fn register_subsignal(self: Rw<Self>, signal: Signal<T>, index: FractionalIndexType) {
        self.subsignals.insert(index, signal);
    }

    pub fn preexisting_subsignal(self: Rw<Self>, index: FractionalIndexType) -> Option<Signal<T>> {
        self.subsignals.get(&index).cloned()
    }

    #[cfg(test)]
    pub fn ordering_subsignals(&self) -> usize {
        self.subsignals.len()
    }
}

impl<T> Meta for SignalInner<T> {
    type Data = ();
}