1use crate::error::Result;
2use crate::store::{Store, StoreId, StoreOptions, StoreState};
3use crate::CollectionMarker;
4use serde::Serialize;
5use tauri::{AppHandle, Emitter as _, EventTarget, Runtime, WebviewWindow, Window};
6
7pub const STORE_CONFIG_CHANGE_EVENT: &str = "tauri-store://config-change";
8pub const STORE_STATE_CHANGE_EVENT: &str = "tauri-store://state-change";
9pub const STORE_UNLOAD_EVENT: &str = "tauri-store://unload";
10
11#[derive(Clone, Debug, Serialize)]
12#[serde(rename_all = "camelCase")]
13pub(crate) struct StatePayload<'a> {
14 id: &'a StoreId,
15 state: &'a StoreState,
16}
17
18impl<'a, R, C> From<&'a Store<R, C>> for StatePayload<'a>
19where
20 R: Runtime,
21 C: CollectionMarker,
22{
23 fn from(store: &'a Store<R, C>) -> Self {
24 Self { id: &store.id, state: store.state() }
25 }
26}
27
28#[derive(Clone, Debug, Serialize)]
29#[serde(rename_all = "camelCase")]
30pub(crate) struct ConfigPayload<'a> {
31 id: &'a StoreId,
32 config: StoreOptions,
33}
34
35impl<'a, R, C> From<&'a Store<R, C>> for ConfigPayload<'a>
36where
37 R: Runtime,
38 C: CollectionMarker,
39{
40 fn from(store: &'a Store<R, C>) -> Self {
41 Self { id: &store.id, config: store.into() }
42 }
43}
44
45pub(crate) fn emit<R, T, S>(app: &AppHandle<R>, event: &str, payload: &T, source: S) -> Result<()>
46where
47 R: Runtime,
48 T: Serialize + ?Sized,
49 S: Into<EventSource>,
50{
51 let source: EventSource = source.into();
52 if let Some(source) = source.0 {
53 emit_filter(app, event, payload, |it| it != source)
54 } else {
55 emit_all(app, event, payload)
56 }
57}
58
59fn emit_all<R, T>(app: &AppHandle<R>, event: &str, payload: &T) -> Result<()>
60where
61 R: Runtime,
62 T: Serialize + ?Sized,
63{
64 app.emit_filter(event, payload, |target| {
65 matches!(target, EventTarget::WebviewWindow { .. })
66 })?;
67
68 Ok(())
69}
70
71fn emit_filter<R, T, F>(app: &AppHandle<R>, event: &str, payload: &T, f: F) -> Result<()>
72where
73 R: Runtime,
74 T: Serialize + ?Sized,
75 F: Fn(&str) -> bool,
76{
77 #[rustfmt::skip]
78 app.emit_filter(event, payload, |target| {
79 matches!(target, EventTarget::WebviewWindow { label } if f(label))
80 })?;
81
82 Ok(())
83}
84
85pub struct EventSource(Option<String>);
87
88impl EventSource {
89 #[inline]
90 pub const fn is_backend(&self) -> bool {
91 self.0.is_none()
92 }
93}
94
95impl From<&str> for EventSource {
96 fn from(source: &str) -> Self {
97 Self(Some(String::from(source)))
98 }
99}
100
101impl From<Option<&str>> for EventSource {
102 fn from(source: Option<&str>) -> Self {
103 Self(source.map(String::from))
104 }
105}
106
107impl From<String> for EventSource {
108 fn from(source: String) -> Self {
109 Self(Some(source))
110 }
111}
112
113impl From<&String> for EventSource {
114 fn from(source: &String) -> Self {
115 Self(Some(source.to_owned()))
116 }
117}
118
119impl From<Option<String>> for EventSource {
120 fn from(source: Option<String>) -> Self {
121 Self(source)
122 }
123}
124
125impl From<&WebviewWindow> for EventSource {
126 fn from(window: &WebviewWindow) -> Self {
127 Self(Some(window.label().to_owned()))
128 }
129}
130
131impl From<&Window> for EventSource {
132 fn from(window: &Window) -> Self {
133 Self(Some(window.label().to_owned()))
134 }
135}