mod effect_fn;
use effect_fn::EffectFn;
use once_cell::sync::Lazy;
use std::{
collections::{HashMap, HashSet},
sync::{Arc, Mutex},
};
pub static LOCK_SET_ACTIVE_EFFECT: Lazy<Mutex<bool>> = Lazy::new(|| Mutex::new(false));
pub static ACTIVE_EFFECT: Lazy<Mutex<Option<Arc<Effect>>>> = Lazy::new(|| Mutex::new(None));
static BUCKET: Lazy<Mutex<HashMap<usize, HashMap<String, Arc<Mutex<HashSet<Arc<EffectFn>>>>>>>> = Lazy::new(|| Mutex::new(HashMap::default()));
#[derive(Clone)]
pub struct Effect(Arc<EffectFn>);
impl core::hash::Hash for Effect {
fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
self.get_effect_fn_ptr_address().hash(state);
}
}
impl std::cmp::PartialEq for Effect {
fn eq(&self, other: &Self) -> bool {
self.get_effect_fn_ptr_address() == other.get_effect_fn_ptr_address()
}
}
impl std::cmp::Eq for Effect {}
impl Effect {
pub fn track<I: crate::syn::rjson::json::Index + ToString + Clone>(target_ptr: usize, index: &I) {
let active_effect = ACTIVE_EFFECT.lock().unwrap();
if active_effect.is_none() {
return;
}
let active_effect = active_effect.as_ref().unwrap();
let mut bucket = BUCKET.lock().unwrap();
let deps_map = bucket.entry(target_ptr).or_insert(HashMap::default());
let deps_set = deps_map.entry(index.to_string()).or_insert(Arc::new(Mutex::new(HashSet::default())));
let mut inner_deps_set = deps_set.lock().unwrap();
if !inner_deps_set.contains(&active_effect.0) {
inner_deps_set.insert(active_effect.0.clone());
active_effect.0.deps_set.lock().unwrap().push(deps_set.clone());
active_effect.0.on_track(target_ptr, index);
}
}
pub fn trigger<I: crate::syn::rjson::json::Index + ToString + Clone>(target_ptr: usize, index: &I) {
let bucket = BUCKET.lock().unwrap();
if let Some(deps_map) = bucket.get(&target_ptr) {
if let Some(deps_set) = deps_map.get(&index.to_string()) {
let inner_deps_set = deps_set.lock().unwrap();
for dep in inner_deps_set.iter() {
dep.on_trigger(target_ptr, index);
dep.run();
}
}
}
}
pub fn remove_target_ptr(target_ptr: usize) {
let mut bucket = BUCKET.lock().unwrap();
if bucket.contains_key(&target_ptr) {
bucket.remove(&target_ptr);
}
}
pub fn replace_target_ptr(target_ptr: usize, new_target_ptr: usize) {
let mut bucket = BUCKET.lock().unwrap();
if let Some(value) = bucket.remove(&target_ptr) {
bucket.insert(new_target_ptr, value);
}
}
}
impl Effect {
pub fn new<F>(closure: F) -> Arc<Effect>
where
F: Fn() -> () + Send + Sync + 'static,
{
let effect = Arc::new(Effect(Arc::new(EffectFn::new(closure))));
*LOCK_SET_ACTIVE_EFFECT.lock().unwrap() = true;
{
*ACTIVE_EFFECT.lock().unwrap() = Some(effect.clone());
}
effect.0.run();
{
*ACTIVE_EFFECT.lock().unwrap() = None;
}
*LOCK_SET_ACTIVE_EFFECT.lock().unwrap() = false;
effect
}
pub fn get_effect_fn_ptr_address(&self) -> usize {
self.0.get_ptr_address()
}
fn cleanup(&mut self) {
let deps_effect_fn = self.0.deps_set.lock().unwrap();
for dep_effect_fn in deps_effect_fn.iter() {
let mut deps_effect_fn = dep_effect_fn.lock().unwrap();
deps_effect_fn.remove(&self.0);
}
}
}
impl Drop for Effect {
fn drop(&mut self) {
log::trace!("drop effect: {:p}, inner effect_fn address {:?}", self, self.get_effect_fn_ptr_address());
self.cleanup();
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn it_works() {
let serde_json_value = serde_json::json!({
"name": "John Doe",
"age": 43,
"phones": [
"+44",
"+44 1234567",
"+44 2345678"
],
"address": {
"street": "123 Main St",
"city": "London",
"state": "UK",
"zip": 12345
}
});
{
let bucket = BUCKET.lock().unwrap();
assert_eq!(bucket.keys().len(), 0);
}
base_test(serde_json_value.clone().into());
{
let bucket = BUCKET.lock().unwrap();
assert_eq!(bucket.keys().len(), 0);
}
object_test(serde_json_value.clone().into());
{
let bucket = BUCKET.lock().unwrap();
assert_eq!(bucket.keys().len(), 0);
}
array_test(serde_json_value.clone().into());
{
let bucket = BUCKET.lock().unwrap();
assert_eq!(bucket.keys().len(), 0);
}
}
fn base_test(json: crate::syn::RJson) {
{
let count = Arc::new(Mutex::new(0));
let eff_json1 = json.clone();
let eff_count = count.clone();
let _eff = crate::syn::effect(move || {
*eff_count.lock().unwrap() += 1;
eff_json1.get("name");
});
{
let bucket = BUCKET.lock().unwrap();
assert_eq!(bucket.keys().len(), 1);
assert_eq!(bucket.get(&json.get_json_ptr_address()).unwrap().keys().len(), 1);
let deps = bucket.get(&json.get_json_ptr_address()).unwrap().get("name").unwrap();
assert_eq!(deps.lock().unwrap().len(), 1);
}
{
assert_eq!(*count.lock().unwrap(), 1);
}
json.set("age2", 0);
json.set("name", "name1");
{
assert_eq!(*count.lock().unwrap(), 2);
}
json.set("name", "name2");
json.set("age", 18);
{
assert_eq!(*count.lock().unwrap(), 3);
}
json.set("name", "name2");
{
assert_eq!(*count.lock().unwrap(), 4);
}
}
{
let bucket = BUCKET.lock().unwrap();
let deps = bucket.get(&json.get_json_ptr_address()).unwrap().get("name").unwrap();
assert_eq!(deps.lock().unwrap().len(), 0);
}
}
fn object_test(json: crate::syn::RJson) {
let count = Arc::new(Mutex::new(0));
let eff_json = json.clone();
let eff_count = count.clone();
let _eff = crate::syn::effect(move || {
*eff_count.lock().unwrap() += 1;
eff_json.get("name");
eff_json.get("address").get("street");
});
{
let bucket = BUCKET.lock().unwrap();
assert_eq!(bucket.keys().len(), 2);
assert_eq!(bucket.get(&json.get_json_ptr_address()).unwrap().keys().len(), 2);
assert_eq!(bucket.get(&json.get("address").get_json_ptr_address()).unwrap().keys().len(), 1);
let deps = bucket.get(&json.get_json_ptr_address()).unwrap().get("name").unwrap();
assert_eq!(deps.lock().unwrap().len(), 1);
let deps = bucket.get(&json.get_json_ptr_address()).unwrap().get("address").unwrap();
assert_eq!(deps.lock().unwrap().len(), 1);
}
{
assert_eq!(*count.lock().unwrap(), 1);
}
json.set("age2", 0);
json.set("name", "name1");
json.set("age", "18");
{
assert_eq!(*count.lock().unwrap(), 2);
}
json.get("address").set("street", "street1");
{
assert_eq!(*count.lock().unwrap(), 3);
}
}
fn array_test(json: crate::syn::RJson) {
let count = Arc::new(Mutex::new(0));
let eff_json1 = json.clone();
let eff_count = count.clone();
let _eff = crate::syn::effect(move || {
*eff_count.lock().unwrap() += 1;
eff_json1.get("name");
eff_json1.get("phones").get(0);
});
{
let bucket = BUCKET.lock().unwrap();
assert_eq!(bucket.keys().len(), 2);
assert_eq!(bucket.get(&json.get_json_ptr_address()).unwrap().keys().len(), 2);
assert_eq!(bucket.get(&json.get("phones").get_json_ptr_address()).unwrap().keys().len(), 1);
let deps = bucket.get(&json.get_json_ptr_address()).unwrap().get("name").unwrap();
assert_eq!(deps.lock().unwrap().len(), 1);
let deps = bucket.get(&json.get_json_ptr_address()).unwrap().get("phones").unwrap();
assert_eq!(deps.lock().unwrap().len(), 1);
}
{
assert_eq!(*count.lock().unwrap(), 1);
}
json.set("age2", 0);
json.set("name", "name1");
json.set("age", "18");
{
assert_eq!(*count.lock().unwrap(), 2);
}
json.get("phones").set(0, "1234567");
{
assert_eq!(*count.lock().unwrap(), 3);
}
json.set("phones", serde_json::json!(["1"]));
{
assert_eq!(*count.lock().unwrap(), 4);
}
json.get("phones").set(0, "2");
{
assert_eq!(*count.lock().unwrap(), 5);
}
}
}