use crate::operation::FunctionSignature;
use std::ops::Deref;
use std::sync::Arc;
use tokio::sync::Mutex;
pub struct Data<T>(Arc<Mutex<T>>);
impl<T> Data<T> {
pub fn new(state: T) -> Data<T> {
Data(Arc::new(Mutex::new(state)))
}
pub async fn clone_inner(&self) -> T
where
T: Clone,
{
self.0.lock().await.clone()
}
pub async fn update<F>(&self, f: F)
where
F: FnOnce(&mut T),
{
let mut lock = self.0.lock().await;
f(&mut *lock);
}
pub async fn set(&self, new_state: T) {
*self.0.lock().await = new_state;
}
pub fn into_inner(self) -> Arc<Mutex<T>> {
self.0
}
}
impl<T> Deref for Data<T> {
type Target = Arc<Mutex<T>>;
fn deref(&self) -> &Arc<Mutex<T>> {
&self.0
}
}
impl<T> Clone for Data<T> {
fn clone(&self) -> Data<T> {
Data(Arc::clone(&self.0))
}
}
impl<T> From<Arc<Mutex<T>>> for Data<T> {
fn from(arc: Arc<Mutex<T>>) -> Self {
Data(arc)
}
}
#[derive(Default, Clone)]
pub struct NoData;
pub trait IntoFunctionParams<F: FunctionSignature> {
fn into_params(self) -> F::Params;
}
macro_rules! impl_into_function_params {
() => {
impl<F> IntoFunctionParams<F> for NoData
where
F: FunctionSignature<Params = ()>
{
fn into_params(self) -> F::Params {
()
}
}
};
($T:ident) => {
impl<$T, F> IntoFunctionParams<F> for Data<$T>
where
F: FunctionSignature<Params = Data<$T>>,
$T: Clone + Send + 'static,
{
fn into_params(self) -> F::Params {
self
}
}
};
($($T:ident),+) => {
impl<$($T,)+ F> IntoFunctionParams<F> for ($(Data<$T>,)+)
where
F: FunctionSignature<Params = ($(Data<$T>,)+)>,
$($T: Clone + Send + 'static,)+
{
fn into_params(self) -> F::Params {
self
}
}
};
}
impl_into_function_params!();
impl_into_function_params!(S1);
impl_into_function_params!(S1, S2);
impl_into_function_params!(S1, S2, S3);
impl_into_function_params!(S1, S2, S3, S4);
#[cfg(test)]
mod tests {
use super::*;
use std::time::Duration;
#[derive(Clone)]
struct User {
name: String,
}
#[derive(Clone)]
struct Config {
timeout: Duration,
}
#[tokio::test]
async fn test_state_operations() {
let state = Data::new(User {
name: "Alice".to_string(),
});
assert_eq!(state.clone_inner().await.name, "Alice");
state.update(|user| user.name = "Bob".to_string()).await;
assert_eq!(state.clone_inner().await.name, "Bob");
state
.set(User {
name: "Charlie".to_string(),
})
.await;
assert_eq!(state.clone_inner().await.name, "Charlie");
}
#[tokio::test]
async fn test_multiple_states() {
let user_state = Data::new(User {
name: "Alice".to_string(),
});
let config_state = Data::new(Config {
timeout: Duration::from_secs(30),
});
let user_clone = user_state.clone();
let config_clone = config_state.clone();
let handle = tokio::spawn(async move {
user_clone
.update(|user| user.name = "Bob".to_string())
.await;
config_clone
.update(|config| config.timeout = Duration::from_secs(60))
.await;
});
let user = user_state.clone_inner().await;
let config = config_state.clone_inner().await;
assert_eq!(user.name, "Alice");
assert_eq!(config.timeout, Duration::from_secs(30));
handle.await.unwrap();
assert_eq!(user_state.clone_inner().await.name, "Bob");
assert_eq!(
config_state.clone_inner().await.timeout,
Duration::from_secs(60)
);
}
#[test]
fn test_into_params() {
let no_state = NoData;
let _: () =
<NoData as IntoFunctionParams<fn() -> std::future::Ready<()>>>::into_params(no_state);
let state = Data::new(User {
name: "Alice".to_string(),
});
let _: Data<User> = <Data<User> as IntoFunctionParams<
fn(Data<User>) -> std::future::Ready<Data<User>>,
>>::into_params(state);
let user_state = Data::new(User {
name: "Bob".to_string(),
});
let config_state = Data::new(Config {
timeout: Duration::from_secs(30),
});
let states = (user_state, config_state);
let _: (Data<User>, Data<Config>) = <(Data<User>, Data<Config>) as IntoFunctionParams<
fn((Data<User>, Data<Config>)) -> std::future::Ready<(Data<User>, Data<Config>)>,
>>::into_params(states);
}
}