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