use std::sync::{MutexGuard, PoisonError};
pub enum SignalType<T: Clone> {
ValidSignal(fn(state: &T)),
InvalidSignal,
}
impl <T: Clone>Clone for SignalType<T> {
fn clone(&self) -> Self {
if let SignalType::ValidSignal(func) = self{
SignalType::ValidSignal(func.clone())
}else{ SignalType::InvalidSignal }
}
}
pub struct StateObject<T: Clone+ PartialEq> {
value: std::sync::Mutex<T>,
signals: Vec<SignalType<T>>,
}
impl<T: Clone + PartialEq> StateObject<T> {
pub fn new(initial_value: T) -> Self {
Self {
value: std::sync::Mutex::new(initial_value),
signals: Vec::new(),
}
}
pub fn new_from(other: &Self) -> Self{
Self::new(other.get())
}
pub fn set(&mut self, new_value: T) -> Result<(), PoisonError<MutexGuard<'_, T>>> {
let mut wrapped = self.value.lock()?;
if (*wrapped) != new_value{
*wrapped = new_value;
for clbks in self.signals.iter(){
if let SignalType::ValidSignal(func) = clbks{
(*func)(&*wrapped)
}
}
}
Ok(())
}
pub fn setter(&mut self, fn_setter: fn(state: T) -> T) -> Result<(), PoisonError<MutexGuard<'_, T>>> {
let value = self.get();
self.set(fn_setter(value))
}
pub fn get(&self) -> T {
self.value.lock().unwrap().clone()
}
pub fn signal(&mut self, sign: fn(state: &T)) -> StateObject<isize> {
self.signals.push(SignalType::ValidSignal(sign));
StateObject::new( (self.signals.len() - 1) as isize )
}
pub fn rm_signal(&mut self, id: &mut StateObject<isize>) -> bool {
if self.signals.len() as isize > id.get() && id.get() > -1 {
self.signals[id.get() as usize] = SignalType::InvalidSignal;
id.set(-1).expect("Error setting new id!");
true
} else {
false
}
}
pub fn flush_signals(&mut self) -> u32{
let mut count = 0u32;
let newvec: Vec<SignalType<T>> = self.signals.iter().filter(|s|{
if let SignalType::InvalidSignal = s{
count += 1;
false
}else{true}
}).cloned().collect();
self.signals = newvec;
count
}
}
impl <T: Clone + PartialEq> Clone for StateObject<T>{
fn clone(&self) -> Self {
let v = self.get();
let mut this = StateObject::new(v);
for sing in self.signals.iter(){
if let SignalType::ValidSignal(func) = sing{
this.signal(*func);
}else{
this.signals.push(SignalType::InvalidSignal);
}
}
this
}
}
pub fn effect<T: Default + Clone + PartialEq>(callback: fn(&T), mut depends: Vec<&mut StateObject<T>>) {
(callback)(&T::default());
depends.iter_mut().for_each(|s| {
s.signal(callback);
});
}
pub fn use_state<T: Clone + PartialEq>(initial_value: T) -> StateObject<T> {
StateObject::new(initial_value)
}
#[cfg(test)]
mod test {
use super::{effect, use_state, StateObject};
#[test]
fn effect_test() {
let mut counter = 0;
let mut state1 = use_state(counter);
counter += 1;
let mut state2 = use_state(counter);
effect(
|state| assert!((*state == 0) || (*state == 1)),
vec![&mut state1, &mut state2],
);
state1.set(1).unwrap();
state2.set(0).unwrap();
}
#[test]
fn clone_test() {
let counter = 0;
let mut state = use_state(counter);
assert_eq!(state.get(), counter);
let mut id = state.signal(|state| {
assert_ne!(*state, -1);
});
let mut id2 = id.clone();
state.set(2).unwrap();
let mut state2 = state.clone();
let state3 = StateObject::new_from(&state);
assert_eq!(state2.get(),state.get());
assert_eq!(state3.get(),state.get());
state2.set(3).unwrap();
assert_ne!(state2.get(),state.get());
assert_eq!(state3.get(),state.get());
assert!(state.rm_signal(&mut id));
state.set(-1).unwrap();
assert!(state2.rm_signal(&mut id2));
state2.set(-1).unwrap();
}
#[test]
fn state_test() {
let mut counter = 0;
let mut state = use_state(counter);
assert_eq!(state.get(), counter);
counter = state.get();
state.set(counter + 2).unwrap();
assert_ne!(counter, state.get());
assert_eq!(counter + 2, state.get());
counter = state.get();
state.setter(|v| v+1).expect("No se pudo acceder al mutex lock!");
assert_ne!(counter, state.get());
}
#[test]
fn signal_test() {
let mut counter = 0;
let mut state = use_state(counter);
let mut id = state.signal(|state| {
assert_ne!(*state, -1);
});
assert_eq!(state.get(), counter);
counter = state.get();
state.set(counter + 2).unwrap();
assert_ne!(counter, state.get());
assert_eq!(counter + 2, state.get());
let mut id2 = state.signal(|state| {
assert_eq!(*state, -1);
});
state.rm_signal(&mut id);
state.set(-1).unwrap();
assert_eq!(state.get(), -1);
assert!(state.rm_signal(&mut id2));
state.flush_signals();
state.set(2).unwrap();
assert_eq!(state.get(), 2);
}
}