sodium-rust 2.1.2

Sodium FRP (Functional Reactive Programming)
Documentation
use crate::impl_::gc_node::{GcNode, Tracer};
use crate::impl_::node::{IsNode, Node};
use crate::impl_::sodium_ctx::SodiumCtx;
use crate::impl_::sodium_ctx::SodiumCtxData;

use std::fmt;
use std::sync::Arc;
use std::sync::Mutex;

pub struct Listener {
    pub data: Arc<Mutex<ListenerData>>,
    pub gc_node: GcNode,
}

impl Clone for Listener {
    fn clone(&self) -> Self {
        self.gc_node.inc_ref();
        Listener {
            data: self.data.clone(),
            gc_node: self.gc_node.clone(),
        }
    }
}

impl Drop for Listener {
    fn drop(&mut self) {
        self.gc_node.dec_ref();
    }
}

pub struct ListenerData {
    pub sodium_ctx: SodiumCtx,
    pub is_weak: bool,
    pub node_op: Option<Node>,
}

impl Listener {
    pub fn new(sodium_ctx: &SodiumCtx, is_weak: bool, node: Node) -> Listener {
        let listener_data = Arc::new(Mutex::new(ListenerData {
            sodium_ctx: sodium_ctx.clone(),
            node_op: Some(node),
            is_weak,
        }));
        let gc_node_desconstructor;
        {
            let listener_data = listener_data.clone();
            gc_node_desconstructor = move || {
                let mut l = listener_data.lock();
                let listener_data = l.as_mut().unwrap();
                listener_data.node_op = None;
            };
        }
        let gc_node_trace;
        {
            let listener_data = listener_data.clone();
            gc_node_trace = move |tracer: &mut Tracer| {
                let mut gc_node_vec;
                {
                    let mut l = listener_data.lock();
                    let listener_data = l.as_mut().unwrap();
                    gc_node_vec = Vec::new();
                    listener_data
                        .node_op
                        .iter()
                        .for_each(|node: &Node| gc_node_vec.push(node.gc_node.clone()));
                }
                gc_node_vec
                    .iter()
                    .for_each(|gc_node: &GcNode| tracer(gc_node));
            };
        }
        let listener = Listener {
            data: listener_data,
            gc_node: GcNode::new(
                &sodium_ctx.gc_ctx(),
                "Listener::new",
                gc_node_desconstructor,
                gc_node_trace,
            ),
        };
        if !is_weak {
            sodium_ctx.with_data(|data: &mut SodiumCtxData| {
                data.keep_alive.push(listener.clone());
            });
        }
        listener
    }

    pub fn unlisten(&self) {
        let is_weak;
        let sodium_ctx;
        {
            let mut l = self.data.lock();
            let data: &mut ListenerData = l.as_mut().unwrap();
            data.node_op = None;
            is_weak = data.is_weak;
            sodium_ctx = data.sodium_ctx.clone();
        }
        if !is_weak {
            sodium_ctx.with_data(|data: &mut SodiumCtxData| {
                data.keep_alive
                    .retain(|l: &Listener| !Arc::ptr_eq(&l.data, &self.data))
            });
        }
    }

    pub fn node_op(&self) -> Option<Node> {
        self.with_data(|data: &mut ListenerData| data.node_op.clone())
    }

    pub fn with_data<R, K: FnOnce(&mut ListenerData) -> R>(&self, k: K) -> R {
        let mut l = self.data.lock();
        let data: &mut ListenerData = l.as_mut().unwrap();
        k(data)
    }
}

impl fmt::Debug for Listener {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let node_op = self.node_op();
        write!(f, "(Listener")?;
        match node_op {
            Some(node) => {
                writeln!(f)?;
                writeln!(f, "{:?})", &node as &(dyn IsNode + Sync + Sync))?;
            }
            None => {
                writeln!(f, ")")?;
            }
        }
        fmt::Result::Ok(())
    }
}