use serde::{Deserialize, Serialize};
#[derive(Debug, Deserialize)]
pub struct LocalNotificationArgs {
pub title: String,
pub body: String,
#[serde(default)]
pub subtitle: Option<String>,
#[serde(default)]
pub sound: Option<String>,
}
#[derive(Debug, Serialize)]
pub struct LocalNotificationResult {
pub delivered: bool,
pub platform: &'static str,
pub backend: &'static str,
}
pub async fn local(args_json: &str) -> Result<String, String> {
let args: LocalNotificationArgs =
serde_json::from_str(args_json).map_err(|e| format!("invalid args: {e}"))?;
validate(&args)?;
#[cfg(target_os = "macos")]
{
deliver_macos(args).await?;
return serde_json::to_string(&LocalNotificationResult {
delivered: true,
platform: "macos",
backend: "user-notification-apple-event",
})
.map_err(|e| format!("serialize: {e}"));
}
#[cfg(target_os = "ios")]
{
let _ = args;
return Err(
"local notifications on iOS must be delivered by the signed host app via UserNotifications"
.to_string(),
);
}
#[cfg(not(any(target_os = "macos", target_os = "ios")))]
{
let _ = args;
Err("local notifications are not supported on this platform".to_string())
}
}
fn validate(args: &LocalNotificationArgs) -> Result<(), String> {
if args.title.trim().is_empty() {
return Err("title is required".to_string());
}
if args.body.trim().is_empty() {
return Err("body is required".to_string());
}
Ok(())
}
#[cfg(target_os = "macos")]
async fn deliver_macos(args: LocalNotificationArgs) -> Result<(), String> {
let script = r#"
on run argv
set notificationTitle to item 1 of argv
set notificationBody to item 2 of argv
set notificationSubtitle to item 3 of argv
set notificationSound to item 4 of argv
if notificationSubtitle is "" and notificationSound is "" then
display notification notificationBody with title notificationTitle
else if notificationSubtitle is "" then
display notification notificationBody with title notificationTitle sound name notificationSound
else if notificationSound is "" then
display notification notificationBody with title notificationTitle subtitle notificationSubtitle
else
display notification notificationBody with title notificationTitle subtitle notificationSubtitle sound name notificationSound
end if
end run
"#;
let subtitle = args.subtitle.unwrap_or_default();
let sound = args.sound.unwrap_or_default();
let argv = [
args.title.as_str(),
args.body.as_str(),
subtitle.as_str(),
sound.as_str(),
];
car_automation::applescript::run_with_args(
script,
car_automation::applescript::Language::AppleScript,
&argv,
Some(std::time::Duration::from_secs(10)),
)
.await
.map(|_| ())
.map_err(|e| format!("{e}"))
}