use crate::{
commands::event_name,
error::Error,
slice::{StateRegistry, SyncState},
};
use std::ops::Deref;
use tauri::{
ipc::{CommandArg, CommandItem, InvokeError},
AppHandle, Emitter, Manager, Runtime, Wry,
};
pub struct Mutator<S: SyncState, R: Runtime = Wry> {
app: AppHandle<R>,
snapshot: S,
}
impl<S: SyncState, R: Runtime> Deref for Mutator<S, R> {
type Target = S;
fn deref(&self) -> &S {
&self.snapshot
}
}
impl<S: SyncState, R: Runtime> Mutator<S, R> {
pub fn current(&self) -> &S {
&self.snapshot
}
pub fn reload(&mut self) -> Result<&S, Error> {
let registry = self.app.state::<StateRegistry>();
self.snapshot = registry.read::<S>()?;
Ok(&self.snapshot)
}
pub fn mutate(&mut self, f: impl FnOnce(&mut S)) {
if let Err(e) = self.try_mutate(f) {
log::error!("[sync-state] mutate<{}> failed: {e}", S::NAME);
}
}
pub fn try_mutate(&mut self, f: impl FnOnce(&mut S)) -> Result<(), Error> {
let registry = self.app.state::<StateRegistry>();
let next = registry.mutate::<S>(f)?;
self.snapshot = next.clone();
self.app
.emit(&event_name(S::NAME), next)
.map_err(|e| Error::Emit(e.to_string()))
}
pub async fn mutate_async<F, Fut>(&mut self, f: F)
where
F: FnOnce(S) -> Fut,
Fut: std::future::Future<Output = S>,
{
if let Err(e) = self.try_mutate_async(f).await {
log::error!("[sync-state] mutate_async<{}> failed: {e}", S::NAME);
}
}
pub async fn try_mutate_async<F, Fut>(&mut self, f: F) -> Result<(), Error>
where
F: FnOnce(S) -> Fut,
Fut: std::future::Future<Output = S>,
{
let current = {
let registry = self.app.state::<StateRegistry>();
registry.read::<S>()?
};
let updated = f(current).await;
let registry = self.app.state::<StateRegistry>();
registry.mutate::<S>(|s| *s = updated.clone())?;
self.snapshot = updated.clone();
self.app
.emit(&event_name(S::NAME), updated)
.map_err(|e| Error::Emit(e.to_string()))
}
}
impl<'de, S: SyncState, R: Runtime> CommandArg<'de, R> for Mutator<S, R> {
fn from_command(command: CommandItem<'de, R>) -> Result<Self, InvokeError> {
let app: AppHandle<R> = command.message.webview().app_handle().clone();
let registry = app
.try_state::<StateRegistry>()
.ok_or_else(|| InvokeError::from(Error::PluginNotInitialized.to_string()))?;
let snapshot = registry
.read::<S>()
.map_err(|e| InvokeError::from(e.to_string()))?;
Ok(Mutator { app, snapshot })
}
}