pytauri_core/ext_mod_impl/lib/
manager.rs1use std::{collections::HashMap, iter::Iterator};
2
3use pyo3::{
4 exceptions::PyValueError,
5 marker::Ungil,
6 prelude::*,
7 types::{PyDict, PyType},
8 FromPyObject, IntoPyObject,
9};
10use pyo3_utils::ungil::UnsafeUngilExt;
11use tauri::{Manager as TauriManager, State};
12
13use crate::{
14 ext_mod::{
15 path::PathResolver,
16 webview::{TauriWebviewWindow, WebviewWindow},
17 App, AppHandle, PyAppHandleExt, TauriApp, TauriAppHandle,
18 },
19 tauri_runtime::Runtime,
20};
21
22pub(crate) struct StateManager(Py<PyDict>);
23
24impl StateManager {
25 pub(crate) fn get_or_init<'a>(
26 py: Python<'_>,
27 manager: &'a impl TauriManager<Runtime>,
28 ) -> State<'a, Self> {
29 if let Some(state) = manager.try_state::<Self>() {
30 return state;
31 }
32 manager.manage(Self(PyDict::new(py).into()));
33 manager.state::<Self>()
34 }
35
36 pub(crate) fn manage(&self, py: Python<'_>, state: &Bound<PyAny>) -> PyResult<bool> {
37 let this = self.0.bind(py);
38 let py_type = state.get_type();
39 if this.contains(&py_type)? {
42 return Ok(false);
43 }
44 this.set_item(py_type, state)?;
45 Ok(true)
46 }
47
48 pub(crate) fn state<'py>(
49 &self,
50 py: Python<'py>,
51 state_type: &Bound<'py, PyType>,
52 ) -> PyResult<Bound<'py, PyAny>> {
53 let state = self.try_state(py, state_type)?;
54 state.ok_or_else(|| {
55 PyValueError::new_err(format!("state() called before manage() for {state_type}"))
56 })
57 }
58
59 pub(crate) fn try_state<'py>(
60 &self,
61 py: Python<'py>,
62 state_type: &Bound<'py, PyType>,
63 ) -> PyResult<Option<Bound<'py, PyAny>>> {
64 let this = self.0.bind(py);
65 PyDictMethods::get_item(this, state_type)
66 }
67}
68
69#[derive(FromPyObject, IntoPyObject, IntoPyObjectRef)]
71#[non_exhaustive]
72pub enum ImplManager {
74 AppHandle(Py<AppHandle>),
75 WebviewWindow(Py<WebviewWindow>),
76 App(Py<App>),
78}
79
80impl ImplManager {
81 #[inline]
82 pub(crate) fn _delegate_app<'py, R>(
83 py: Python<'py>,
84 app: &Py<App>,
85 f: impl FnOnce(Python<'py>, &TauriApp) -> R,
86 ) -> PyResult<R> {
87 let py_app = app.borrow(py);
88 let rs_app = py_app.0.try_lock_inner_ref()??;
89 Ok(f(py, &rs_app))
90 }
91
92 #[inline]
93 pub(crate) fn _delegate_app_handle<'py, R>(
94 py: Python<'py>,
95 app_handle: &Py<AppHandle>,
96 f: impl FnOnce(Python<'py>, &TauriAppHandle) -> R,
97 ) -> PyResult<R> {
98 let app_handle = app_handle.get().0.inner_ref();
99 Ok(f(py, &app_handle))
100 }
101
102 #[inline]
103 pub(crate) fn _delegate_webview_window<'py, R>(
104 py: Python<'py>,
105 webview_window: &Py<WebviewWindow>,
106 f: impl FnOnce(Python<'py>, &TauriWebviewWindow) -> R,
107 ) -> PyResult<R> {
108 let webview_window = webview_window.get().0.inner_ref();
109 Ok(f(py, &webview_window))
110 }
111
112 #[inline]
113 pub(crate) fn _delegate_manager_ungil<M, F, R>(py: Python<'_>, manager: &M, f: F) -> R
114 where
115 M: tauri::Manager<Runtime>,
116 F: FnOnce(&M) -> R + Ungil + Send,
117 R: Ungil,
118 {
119 unsafe {
120 py.allow_threads_unsend(manager, f)
122 }
123 }
124}
125
126macro_rules! manager_method_impl {
144 ($py:expr, $manager:expr, $f0:expr, $f1:expr, $f2:expr) => {{
146 use $crate::ext_mod::ImplManager;
147
148 let manager: &ImplManager = $manager;
149 match manager {
150 ImplManager::App(v) => {
151 ImplManager::_delegate_app($py, v, $f0)
152 }
153 ImplManager::AppHandle(v) => {
154 ImplManager::_delegate_app_handle($py, v, $f1)
155 }
156 ImplManager::WebviewWindow(v) => {
157 ImplManager::_delegate_webview_window($py, v, $f2)
158 }
159 }
160 }};
161
162 ($py:expr, $manager:expr, [ungil], $($f:tt)*) => {{
164 manager_method_impl!($py, $manager, |py, manager| {
165 $crate::ext_mod::ImplManager::_delegate_manager_ungil(py, manager, $($f)*)
166 })
167 }};
168 ($py:expr, $manager:expr, $($f:tt)*) => {
170 manager_method_impl!($py, $manager, $($f)*, $($f)*, $($f)*)
171 };
172}
173
174pub(crate) use manager_method_impl;
176
177#[pyclass(frozen)]
179#[non_exhaustive]
180pub struct Manager;
181
182#[pymethods]
183impl Manager {
184 #[staticmethod]
185 fn app_handle(py: Python<'_>, slf: ImplManager) -> PyResult<Py<AppHandle>> {
186 manager_method_impl!(py, &slf, |py, manager| manager
187 .py_app_handle()
189 .clone_ref(py))
190 }
191
192 #[staticmethod]
193 fn get_webview_window(
194 py: Python<'_>,
195 slf: ImplManager,
196 label: &str,
197 ) -> PyResult<Option<WebviewWindow>> {
198 manager_method_impl!(py, &slf, [ungil], |manager| {
199 manager.get_webview_window(label).map(WebviewWindow::new)
200 })
201 }
202
203 #[staticmethod]
204 fn webview_windows(
205 py: Python<'_>,
206 slf: ImplManager,
207 ) -> PyResult<HashMap<String, WebviewWindow>> {
208 manager_method_impl!(py, &slf, [ungil], |manager| {
209 manager
210 .webview_windows()
211 .into_iter()
212 .map(|(label, window)| (label, WebviewWindow::new(window)))
213 .collect::<_>()
214 })
215 }
216
217 #[staticmethod]
218 fn manage(py: Python<'_>, slf: ImplManager, state: &Bound<PyAny>) -> PyResult<bool> {
219 manager_method_impl!(py, &slf, |py, manager| {
220 let state_manager = StateManager::get_or_init(py, manager);
221 state_manager.manage(py, state)
222 })?
223 }
224
225 #[staticmethod]
226 fn state<'py>(
227 py: Python<'py>,
228 slf: ImplManager,
229 state_type: &Bound<'py, PyType>,
230 ) -> PyResult<Bound<'py, PyAny>> {
231 manager_method_impl!(py, &slf, |py, manager| {
232 let state_manager = StateManager::get_or_init(py, manager);
233 state_manager.state(py, state_type)
234 })?
235 }
236
237 #[staticmethod]
238 fn try_state<'py>(
239 py: Python<'py>,
240 slf: ImplManager,
241 state_type: &Bound<'py, PyType>,
242 ) -> PyResult<Option<Bound<'py, PyAny>>> {
243 manager_method_impl!(py, &slf, |py, manager| {
244 let state_manager = StateManager::get_or_init(py, manager);
245 state_manager.try_state(py, state_type)
246 })?
247 }
248
249 #[staticmethod]
250 fn path(py: Python<'_>, slf: ImplManager) -> PyResult<PathResolver> {
251 manager_method_impl!(py, &slf, [ungil], |manager| {
252 let path_resolver = manager.path().clone();
253 PathResolver::new(path_resolver)
254 })
255 }
256}