amethystate-core 0.9.1

Internal crate for amethystate, not intended for direct use
Documentation
use crate::async_impl::{AsyncSubscriptionBackend, SubscriptionHandle};
use crate::primitives::field_core::FieldValue;
use crate::{Change, FieldCore, InterceptDisposer, SignalSubscription};
use serde::Serialize;
use serde::de::DeserializeOwned;
use std::sync::{Arc, Mutex};
use uuid::Uuid;

pub struct Field<T, B> {
    pub core: FieldCore<T>,
    pub path: Arc<str>,
    pub instance_id: Uuid,
    _subscription: Arc<Mutex<SubscriptionHandle>>,
    backend: B,
}

impl<T, B> Clone for Field<T, B>
where
    B: Clone,
{
    fn clone(&self) -> Self {
        Self {
            core: self.core.clone(),
            path: self.path.clone(),
            instance_id: self.instance_id,
            _subscription: self._subscription.clone(),
            backend: self.backend.clone(),
        }
    }
}

impl<T, B> PartialEq for Field<T, B> {
    fn eq(&self, other: &Self) -> bool {
        self.path == other.path && Arc::ptr_eq(&self.core.signal.value, &other.core.signal.value)
    }
}

impl<T, B> Eq for Field<T, B> {}

impl<T, B> std::fmt::Debug for Field<T, B>
where
    T: std::fmt::Debug + Clone + 'static,
{
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.debug_struct("Field")
            .field("path", &self.path)
            .field("value", &self.core.get())
            .finish()
    }
}

impl<T, B> Field<T, B>
where
    T: FieldValue,
    B: AsyncSubscriptionBackend,
{
    pub fn fork(&self) -> Self {
        self.fork_with_id(Uuid::new_v4())
    }

    pub fn fork_with_id(&self, new_instance_id: Uuid) -> Self {
        Self {
            core: self.core.clone(),
            path: self.path.clone(),
            instance_id: new_instance_id,
            _subscription: self._subscription.clone(),
            backend: self.backend.clone(),
        }
    }

    pub fn new(key: impl Into<Arc<str>>, initial_value: T) -> Self
    where
        B: Default,
    {
        Self::new_with_backend(key, initial_value, B::default())
    }

    pub fn new_with_backend(key: impl Into<Arc<str>>, initial_value: T, backend: B) -> Self {
        Self::new_with_backend_and_id(key, initial_value, backend, Uuid::new_v4())
    }

    pub fn new_with_backend_and_id(
        key: impl Into<Arc<str>>,
        initial_value: T,
        backend: B,
        instance_id: Uuid,
    ) -> Self {
        let path = key.into();
        let core = FieldCore::new(initial_value);
        let subscription = backend.subscribe_field(path.clone(), core.clone());

        Self {
            core,
            path,
            instance_id,
            _subscription: Arc::new(Mutex::new(subscription)),
            backend,
        }
    }

    pub fn value(&self) -> T {
        self.core.get()
    }

    pub async fn get(&self) -> Result<T, B::Error> {
        self.backend
            .get(&self.path)
            .await?
            .ok_or_else(|| self.backend.key_not_found(self.path.to_string()))
    }

    pub async fn update<F>(&self, f: F) -> Result<T, B::Error>
    where
        F: FnOnce(T) -> T,
    {
        let val = self.get().await?;
        let new_val = f(val);
        self.set(new_val.clone()).await?;
        Ok(new_val)
    }

    pub async fn modify<F>(&self, f: F) -> Result<(), B::Error>
    where
        F: FnOnce(&mut T),
    {
        let mut val = self.get().await?;
        f(&mut val);
        self.set(val).await
    }

    pub async fn set(&self, value: T) -> Result<(), B::Error> {
        crate::field_set_async(
            &self.backend,
            &self.core,
            self.path.clone(),
            value,
            Some(self.instance_id),
        )
        .await
    }

    pub fn subscribe<F>(&self, callback: F) -> SignalSubscription
    where
        F: Fn(T) + Send + Sync + 'static,
    {
        self.core.subscribe(callback)
    }

    pub fn subscribe_external<F>(&self, callback: F) -> SignalSubscription
    where
        F: Fn(T) + Send + Sync + 'static,
    {
        let my_id = self.instance_id;
        self.core.subscribe_with_source(move |val, src| {
            if src != Some(my_id) {
                callback(val);
            }
        })
    }

    pub fn intercept<F>(&self, callback: F) -> InterceptDisposer
    where
        F: Fn(Change<T>) -> Option<Change<T>> + Send + Sync + 'static,
    {
        self.core.intercept(self.path.clone(), callback)
    }
}

impl<T, B> crate::pipeline::Reactive<T> for Field<T, B>
where
    T: Serialize + DeserializeOwned + Clone + Send + Sync + 'static,
    B: AsyncSubscriptionBackend,
{
    fn get(&self) -> T {
        self.core.get()
    }

    fn subscribe_with_source<F>(&self, callback: F) -> SignalSubscription
    where
        F: Fn(T, Option<Uuid>) + Send + Sync + 'static,
    {
        self.core.subscribe_with_source(callback)
    }
}