use std::{
error::Error,
fmt::{Debug, Display, Formatter},
};
use pyo3::{prelude::*, types::PyDict};
use pyo3_utils::{
from_py_dict::{derive_from_py_dict, FromPyDict as _, NotRequired},
py_wrapper::{PyWrapper, PyWrapperT2},
};
use tauri_plugin_notification::{self as plugin, NotificationExt as _};
use crate::{
ext_mod::{manager_method_impl, plugin::Plugin, ImplManager},
tauri_runtime::Runtime,
};
#[derive(Debug)]
struct PluginError(plugin::Error);
impl Display for PluginError {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
Display::fmt(&self.0, f)
}
}
impl Error for PluginError {}
impl From<PluginError> for PyErr {
fn from(value: PluginError) -> Self {
match value.0 {
plugin::Error::Io(e) => e.into(),
}
}
}
impl From<plugin::Error> for PluginError {
fn from(value: plugin::Error) -> Self {
Self(value)
}
}
#[pyfunction]
pub fn init() -> Plugin {
Plugin::new(Box::new(|| Box::new(plugin::init::<Runtime>())))
}
#[non_exhaustive]
pub struct NotificationBuilderArgs {
id: NotRequired<i32>,
channel_id: NotRequired<String>,
title: NotRequired<String>,
body: NotRequired<String>,
large_body: NotRequired<String>,
summary: NotRequired<String>,
action_type_id: NotRequired<String>,
group: NotRequired<String>,
group_summary: bool,
sound: NotRequired<String>,
inbox_line: NotRequired<String>,
icon: NotRequired<String>,
large_icon: NotRequired<String>,
icon_color: NotRequired<String>,
ongoing: bool,
auto_cancel: bool,
silent: bool,
}
derive_from_py_dict!(NotificationBuilderArgs {
#[pyo3(default)]
id,
#[pyo3(default)]
channel_id,
#[pyo3(default)]
title,
#[pyo3(default)]
body,
#[pyo3(default)]
large_body,
#[pyo3(default)]
summary,
#[pyo3(default)]
action_type_id,
#[pyo3(default)]
group,
#[pyo3(default)]
group_summary,
#[pyo3(default)]
sound,
#[pyo3(default)]
inbox_line,
#[pyo3(default)]
icon,
#[pyo3(default)]
large_icon,
#[pyo3(default)]
icon_color,
#[pyo3(default)]
ongoing,
#[pyo3(default)]
auto_cancel,
#[pyo3(default)]
silent,
});
impl NotificationBuilderArgs {
fn from_kwargs(kwargs: Option<&Bound<'_, PyDict>>) -> PyResult<Option<Self>> {
kwargs
.map(NotificationBuilderArgs::from_py_dict)
.transpose()
}
fn apply_to_builder(
self,
mut builder: plugin::NotificationBuilder<Runtime>,
) -> plugin::NotificationBuilder<Runtime> {
let Self {
id,
channel_id,
title,
body,
large_body,
summary,
action_type_id,
group,
group_summary,
sound,
inbox_line,
icon,
large_icon,
icon_color,
ongoing,
auto_cancel,
silent,
} = self;
if let Some(id) = id.0 {
builder = builder.id(id);
}
if let Some(channel_id) = channel_id.0 {
builder = builder.channel_id(channel_id);
}
if let Some(title) = title.0 {
builder = builder.title(title);
}
if let Some(body) = body.0 {
builder = builder.body(body);
}
if let Some(large_body) = large_body.0 {
builder = builder.large_body(large_body);
}
if let Some(summary) = summary.0 {
builder = builder.summary(summary);
}
if let Some(action_type_id) = action_type_id.0 {
builder = builder.action_type_id(action_type_id);
}
if let Some(group) = group.0 {
builder = builder.group(group);
}
if group_summary {
builder = builder.group_summary();
}
if let Some(sound) = sound.0 {
builder = builder.sound(sound);
}
if let Some(inbox_line) = inbox_line.0 {
builder = builder.inbox_line(inbox_line);
}
if let Some(icon) = icon.0 {
builder = builder.icon(icon);
}
if let Some(large_icon) = large_icon.0 {
builder = builder.large_icon(large_icon);
}
if let Some(icon_color) = icon_color.0 {
builder = builder.icon_color(icon_color);
}
if ongoing {
builder = builder.ongoing();
}
if auto_cancel {
builder = builder.auto_cancel();
}
if silent {
builder = builder.silent();
}
builder
}
}
#[pyclass(frozen)]
#[non_exhaustive]
pub struct NotificationBuilder(pub PyWrapper<PyWrapperT2<plugin::NotificationBuilder<Runtime>>>);
impl NotificationBuilder {
fn new(builder: plugin::NotificationBuilder<Runtime>) -> Self {
Self(PyWrapper::new2(builder))
}
}
#[pymethods]
impl NotificationBuilder {
#[pyo3(signature = (**kwargs))]
fn show(&self, kwargs: Option<&Bound<'_, PyDict>>) -> PyResult<()> {
let args = NotificationBuilderArgs::from_kwargs(kwargs)?;
let mut builder = self.0.try_take_inner()??;
if let Some(args) = args {
builder = args.apply_to_builder(builder);
}
builder
.show()
.map_err(PluginError::from)
.map_err(PyErr::from)
}
}
#[pyclass(frozen)]
#[non_exhaustive]
pub struct NotificationExt;
pub type ImplNotificationExt = ImplManager;
#[pymethods]
impl NotificationExt {
#[staticmethod]
fn builder(slf: ImplNotificationExt, py: Python<'_>) -> PyResult<NotificationBuilder> {
manager_method_impl!(py, &slf, |_py, manager| {
let builder = manager.notification().builder();
Ok(NotificationBuilder::new(builder))
})?
}
}
#[pymodule(submodule, gil_used = false)]
pub mod notification {
#[pymodule_export]
pub use super::{init, NotificationBuilder, NotificationExt};
pub use super::{ImplNotificationExt, NotificationBuilderArgs};
}