bloom-html 0.1.3

Provides the HtmlNode type for bloom-client and bloom-server
Documentation
use std::{
    cell::RefCell,
    collections::HashMap,
    hash::Hash,
    ptr,
    sync::atomic::{AtomicU16, Ordering},
};

/// Represents a reference to a DOM element.
/// Obtain this via
/// ```
/// use_ref::<DomRef>();
/// ```
#[derive(Debug, Default)]
pub struct DomRef(AtomicU16);

thread_local! {
    static HTML_ELEMENT_MAP: RefCell<HashMap<u16, web_sys::Node>> = RefCell::new(HashMap::new());
}

impl DomRef {
    pub fn set(&self, element: web_sys::Node) {
        HTML_ELEMENT_MAP.with(|map| {
            let mut map = map.borrow_mut();
            let current_key = self.0.load(Ordering::Relaxed);
            let key = if current_key != 0 {
                current_key
            } else {
                let mut key = 1;
                while map.contains_key(&key) {
                    key += 1;
                    if key == u16::MAX {
                        panic!("Element Map Overflow");
                    }
                }
                self.0.store(key, Ordering::Relaxed);
                key
            };
            map.insert(key, element);
        });
    }

    /// use this method in an effect to get the actual DOM element:
    /// ```
    /// let my_ref = use_ref::<DomRef>();
    /// use_effect(my_ref, |my_ref| {
    ///     let dom_element = my_ref.get();
    ///     // do something with dom_element
    /// })
    /// ```
    pub fn get(&self) -> Option<web_sys::Node> {
        HTML_ELEMENT_MAP.with(|map| {
            map.borrow()
                .get(&self.0.load(Ordering::Relaxed))
                .map(|element| element.clone())
        })
    }
}

impl Drop for DomRef {
    fn drop(&mut self) {
        HTML_ELEMENT_MAP.with(|map| {
            map.borrow_mut().remove(&self.0.load(Ordering::Relaxed));
        });
    }
}

impl Hash for DomRef {
    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
        self.0.load(Ordering::Relaxed).hash(state);
    }
}

impl PartialEq for DomRef {
    fn eq(&self, other: &Self) -> bool {
        ptr::addr_eq(self.0.as_ptr(), other.0.as_ptr())
    }
}