pytauri_core/ext_mod_impl/lib/
manager.rs

1use std::{collections::HashMap, iter::Iterator};
2
3use pyo3::{marker::Ungil, prelude::*, FromPyObject, IntoPyObject};
4use pyo3_utils::ungil::UnsafeUngilExt;
5use tauri::Manager as _;
6
7use crate::{
8    ext_mod::{
9        path::PathResolver,
10        webview::{TauriWebviewWindow, WebviewWindow},
11        App, AppHandle, PyAppHandleExt, TauriApp, TauriAppHandle,
12    },
13    tauri_runtime::Runtime,
14};
15
16/// The Implementers of [tauri::Manager].
17#[derive(FromPyObject, IntoPyObject, IntoPyObjectRef)]
18#[non_exhaustive]
19// TODO: more types
20pub enum ImplManager {
21    App(Py<App>),
22    AppHandle(Py<AppHandle>),
23    WebviewWindow(Py<WebviewWindow>),
24}
25
26impl ImplManager {
27    #[inline]
28    pub(crate) fn _delegate_app<'py, R>(
29        py: Python<'py>,
30        app: &Py<App>,
31        f: impl FnOnce(Python<'py>, &TauriApp) -> R,
32    ) -> PyResult<R> {
33        let py_app = app.borrow(py);
34        let rs_app = py_app.0.try_lock_inner_ref()??;
35        Ok(f(py, &rs_app))
36    }
37
38    #[inline]
39    pub(crate) fn _delegate_app_handle<'py, R>(
40        py: Python<'py>,
41        app_handle: &Py<AppHandle>,
42        f: impl FnOnce(Python<'py>, &TauriAppHandle) -> R,
43    ) -> PyResult<R> {
44        let app_handle = app_handle.get().0.inner_ref();
45        Ok(f(py, &app_handle))
46    }
47
48    #[inline]
49    pub(crate) fn _delegate_webview_window<'py, R>(
50        py: Python<'py>,
51        webview_window: &Py<WebviewWindow>,
52        f: impl FnOnce(Python<'py>, &TauriWebviewWindow) -> R,
53    ) -> PyResult<R> {
54        let webview_window = webview_window.get().0.inner_ref();
55        Ok(f(py, &webview_window))
56    }
57
58    #[inline]
59    pub(crate) fn _delegate_manager_ungil<M, F, R>(py: Python<'_>, manager: &M, f: F) -> R
60    where
61        M: tauri::Manager<Runtime>,
62        F: FnOnce(&M) -> R + Ungil + Send,
63        R: Ungil,
64    {
65        unsafe {
66            // safety: `tauri::Manager` does not hold the GIL, so this is safe
67            py.allow_threads_unsend(manager, f)
68        }
69    }
70}
71
72/**
73```ignore
74fn manager_method_impl(py: Python<'_>, manager: &ImplManager) -> PyResult<()> {
75    manager_method_impl!(py, manager, |_py, manager| {
76        // here the `manager` is equivalent to `&impl tauri::Manager<Runtime>`
77        manager.get_webview_window("main")
78    })?;
79
80    manager_method_impl!(py, manager, [ungil], |manager| {
81        // here equivalent to `Python::allow_threads_unsend`
82        manager.get_webview_window("main")
83    })?;
84
85    Ok(())
86}
87```
88*/
89macro_rules! manager_method_impl {
90    // impl
91    ($py:expr, $manager:expr, $f0:expr, $f1:expr, $f2:expr) => {{
92        use $crate::ext_mod::ImplManager;
93
94        let manager: &ImplManager = $manager;
95        match manager {
96            ImplManager::App(v) => {
97                ImplManager::_delegate_app($py, v, $f0)
98            }
99            ImplManager::AppHandle(v) => {
100                ImplManager::_delegate_app_handle($py, v, $f1)
101            }
102            ImplManager::WebviewWindow(v) => {
103                ImplManager::_delegate_webview_window($py, v, $f2)
104            }
105        }
106    }};
107
108    // entry1 -> entry0
109    ($py:expr, $manager:expr, [ungil], $($f:tt)*) => {{
110        manager_method_impl!($py, $manager, |py, manager| {
111            $crate::ext_mod::ImplManager::_delegate_manager_ungil(py, manager, $($f)*)
112        })
113    }};
114    // entry0
115    ($py:expr, $manager:expr, $($f:tt)*) => {
116        manager_method_impl!($py, $manager, $($f)*, $($f)*, $($f)*)
117    };
118}
119
120// if export this macro, remember to enable doctest 👆
121pub(crate) use manager_method_impl;
122
123/// See also: [tauri::Manager].
124#[pyclass(frozen)]
125#[non_exhaustive]
126pub struct Manager;
127
128#[pymethods]
129impl Manager {
130    #[staticmethod]
131    fn app_handle(py: Python<'_>, slf: ImplManager) -> PyResult<Py<AppHandle>> {
132        manager_method_impl!(py, &slf, |py, manager| manager
133            // TODO, PERF: release the GIL?
134            .py_app_handle()
135            .clone_ref(py))
136    }
137
138    #[staticmethod]
139    fn get_webview_window(
140        py: Python<'_>,
141        slf: ImplManager,
142        label: &str,
143    ) -> PyResult<Option<WebviewWindow>> {
144        manager_method_impl!(py, &slf, [ungil], |manager| {
145            manager.get_webview_window(label).map(WebviewWindow::new)
146        })
147    }
148
149    #[staticmethod]
150    fn webview_windows(
151        py: Python<'_>,
152        slf: ImplManager,
153    ) -> PyResult<HashMap<String, WebviewWindow>> {
154        manager_method_impl!(py, &slf, [ungil], |manager| {
155            manager
156                .webview_windows()
157                .into_iter()
158                .map(|(label, window)| (label, WebviewWindow::new(window)))
159                .collect::<_>()
160        })
161    }
162
163    #[staticmethod]
164    fn path(py: Python<'_>, slf: ImplManager) -> PyResult<PathResolver> {
165        manager_method_impl!(py, &slf, [ungil], |manager| {
166            let path_resolver = manager.path().clone();
167            PathResolver::new(path_resolver)
168        })
169    }
170}