pytauri_core/ext_mod_impl/lib/
url.rs

1use std::{borrow::Cow, convert::Infallible, path::PathBuf};
2
3use pyo3::{exceptions::PyValueError, prelude::*, types::PyString, FromPyObject, IntoPyObject};
4
5use crate::utils::non_exhaustive_panic;
6
7type TauriUrl = tauri::Url;
8
9/// See also: [tauri::Url]
10pub struct Url<'a>(pub Cow<'a, TauriUrl>);
11
12impl From<TauriUrl> for Url<'_> {
13    fn from(url: TauriUrl) -> Self {
14        Self(Cow::Owned(url))
15    }
16}
17impl<'a> From<&'a TauriUrl> for Url<'a> {
18    fn from(url: &'a TauriUrl) -> Self {
19        Self(Cow::Borrowed(url))
20    }
21}
22
23impl From<Url<'_>> for TauriUrl {
24    fn from(url: Url<'_>) -> Self {
25        url.0.into_owned()
26    }
27}
28
29impl AsRef<TauriUrl> for Url<'_> {
30    fn as_ref(&self) -> &TauriUrl {
31        &self.0
32    }
33}
34
35impl<'py> FromPyObject<'py> for Url<'_> {
36    fn extract_bound(ob: &Bound<'py, PyAny>) -> PyResult<Self> {
37        // TODO, PERF: once we drop py39, we can use `&str` directly
38        let url: Cow<'_, str> = ob.extract()?;
39        // TODO: unify this error type
40        let url = TauriUrl::parse(&url).map_err(|e| PyValueError::new_err(e.to_string()))?;
41        Ok(Self::from(url))
42    }
43}
44
45impl<'py> IntoPyObject<'py> for &Url<'_> {
46    type Target = PyString;
47    type Output = Bound<'py, Self::Target>;
48    type Error = Infallible;
49
50    fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
51        let url = PyString::new(py, self.0.as_str());
52        Ok(url)
53    }
54}
55
56impl<'py> IntoPyObject<'py> for Url<'_> {
57    type Target = PyString;
58    type Output = Bound<'py, Self::Target>;
59    type Error = Infallible;
60
61    fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
62        (&self).into_pyobject(py)
63    }
64}
65
66/// See also: [tauri::WebviewUrl]
67#[pyclass(frozen)]
68#[non_exhaustive]
69pub enum WebviewUrl {
70    External(Url<'static>),
71    App(PathBuf),
72    CustomProtocol(Url<'static>),
73    _NonExhaustive(),
74}
75
76impl WebviewUrl {
77    #[expect(dead_code)] // TODO
78    pub(crate) fn from_tauri(value: tauri::WebviewUrl) -> Self {
79        match value {
80            tauri::WebviewUrl::External(url) => Self::External(url.into()),
81            tauri::WebviewUrl::App(path) => Self::App(path),
82            tauri::WebviewUrl::CustomProtocol(url) => Self::CustomProtocol(url.into()),
83            _ => Self::_NonExhaustive(),
84        }
85    }
86
87    pub(crate) fn to_tauri(&self) -> PyResult<tauri::WebviewUrl> {
88        // TODO, XXX, FIXME: avoid clone
89        let value = match self {
90            Self::External(url) => tauri::WebviewUrl::External(url.0.clone().into_owned()),
91            Self::App(path) => tauri::WebviewUrl::App(path.clone()),
92            Self::CustomProtocol(url) => {
93                tauri::WebviewUrl::CustomProtocol(url.0.clone().into_owned())
94            }
95            Self::_NonExhaustive() => non_exhaustive_panic(),
96        };
97        Ok(value)
98    }
99}