use std::{convert::Infallible, str::FromStr}; use pyo3::{types::PyString, Bound, IntoPyObject, Python}; use python_utils::IntoPyObjectViaDisplay; use snafu::Snafu; use strum::EnumString; use url::Url; pub mod command; pub mod standard; pub mod text_to_speech; pub use command::*; pub use standard::StandardNotification; pub use text_to_speech::TextToSpeech; #[derive(Debug, Clone, derive_more::Display)] pub struct NonSpecialMessage(String); #[derive(Debug, Clone, EnumString, strum::Display)] #[strum(serialize_all = "snake_case")] pub enum SpecialMessage { ClearBadge, ClearNotification, CommandActivity, CommandAppLock, CommandAutoScreenBrightness, CommandBluetooth, CommandBleTransmitter, CommandBeaconMonitor, CommandBroadcastIntent, CommandDnd, CommandFlashlight, CommandHighAccuracyMode, CommandLaunchApp, CommandMedia, CommandRingerMode, CommandScreenBrightnessLevel, CommandScreenOffTimeout, CommandScreenOn, CommandStopTts, CommandPersistentConnection, CommandUpdateSensors, CommandVolumeLevel, CommandWebivew, RemoveChannel, RequestLocationUpdate, // #[strum(serialize = "TTS")] TODO: WIP: TESTING Tts, UpdateComplications, UpdateWidgets, } /// wasn't supposed to get a specially-behaving message here, but got {got} #[derive(Debug, Clone, Snafu)] pub struct WasSpecialMessage { pub got: SpecialMessage, } impl FromStr for NonSpecialMessage { type Err = WasSpecialMessage; fn from_str(s: &str) -> Result { match SpecialMessage::from_str(s) { Ok(special_message) => Err(WasSpecialMessage { got: special_message, }), Err(_e) => Ok(NonSpecialMessage(s.into())), } } } /// How much of a notification is visible on the lock screen #[derive(Debug, Clone, Default, EnumString, strum::Display)] #[strum(serialize_all = "snake_case")] pub enum Visibility { /// always show all notification content Public, /// visibility depends on your setting in the system Settings app > Notifications; /// if the option to show sensitive notifications when locked is enabled all notification content will be shown, /// otherwise only basic information such as the icon and app name are visible #[default] Private, /// always hide notification from lock screen Secret, } // TODO: replace with a derive(DisplayToPy) (analogous to serde_with::SerializeDisplay) once I make one impl<'py> IntoPyObject<'py> for Visibility { type Target = PyString; type Output = Bound<'py, Self::Target>; type Error = Infallible; fn into_pyobject(self, py: Python<'py>) -> Result { let s = self.to_string(); s.into_pyobject(py) } } #[derive(Debug, Clone, EnumString, strum::Display)] pub enum Behavior { /// prompt for text to return with the event #[strum(serialize = "textInput")] TextInput, } // TODO: replace with a derive(DisplayToPy) (analogous to serde_with::SerializeDisplay) once I make one impl<'py> IntoPyObject<'py> for Behavior { type Target = PyString; type Output = Bound<'py, Self::Target>; type Error = Infallible; fn into_pyobject(self, py: Python<'py>) -> Result { let s = self.to_string(); s.into_pyobject(py) } } #[derive(Debug, Clone, Default, EnumString, strum::Display)] #[strum(serialize_all = "camelCase")] pub enum ActivationMode { /// launch the app when tapped Foreground, /// just fires the event #[default] Background, } // TODO: replace with a derive(DisplayToPy) (analogous to serde_with::SerializeDisplay) once I make one impl<'py> IntoPyObject<'py> for ActivationMode { type Target = PyString; type Output = Bound<'py, Self::Target>; type Error = Infallible; fn into_pyobject(self, py: Python<'py>) -> Result { let s = self.to_string(); s.into_pyobject(py) } } // TODO: better typed versions like `CallNumber` or `OpenWebpage` where Action: From and Action: From #[derive(Debug, Clone, IntoPyObject, typed_builder::TypedBuilder)] #[builder(field_defaults(default, setter(strip_option(fallback_suffix = "_option"))))] pub struct Action { // TODO: proper type // TODO: I wish I could call this identifier or something instead #[builder(!default, setter(!strip_option))] pub action: String, #[builder(!default, setter(!strip_option))] pub title: String, pub uri: Option>, pub behavior: Option, // TODO: make this written as activationMode /// (iOS only) decide whether to open the app (to the foreground) when tapped /// or merely fire an event (in the background) pub activation_mode: Option, // TODO: make this written as authenticationRequired /// (iOS only) require entering a passcode to use the action pub authentication_required: Option, /// (iOS only) color the action's title red, indicating a destructive action pub destructive: Option, // TODO: make this written as textInputButtonTitle /// (iOS only) Title to use for text input for actions that prompt pub text_input_button_title: Option, // TODO: make this written as textInputPlaceholder /// (iOS only) Placeholder to use for text input for actions that prompt pub text_input_placeholder: Option, // TODO: proper type /// (iOS only) icon to use for the notification pub icon: Option, } #[derive(Debug, Clone, Default, EnumString, strum::Display)] #[strum(serialize_all = "snake_case", suffix = "_stream")] pub enum MediaStream { Alarm, Call, Dtmf, #[default] Music, Notification, Ring, System, } // TODO: replace with a derive(DisplayToPy) (analogous to serde_with::SerializeDisplay) once I make one impl<'py> IntoPyObject<'py> for MediaStream { type Target = PyString; type Output = Bound<'py, Self::Target>; type Error = Infallible; fn into_pyobject(self, py: Python<'py>) -> Result { let s = self.to_string(); s.into_pyobject(py) } } #[derive(Debug, Default, Clone, IntoPyObject, typed_builder::TypedBuilder)] #[builder(field_defaults(default, setter(strip_option(fallback_suffix = "_option"))))] pub struct NotifyMobileAppServiceDataData { actions: Option>, media_stream: Option, tts_text: Option, visibility: Option, } #[derive(Debug, Clone, IntoPyObject, typed_builder::TypedBuilder)] #[builder(field_defaults(default, setter(strip_option(fallback_suffix = "_option"))))] pub struct NotifyMobileAppServiceData { #[builder(!default, setter(!strip_option))] message: String, title: Option, #[builder(setter(!strip_option))] data: NotifyMobileAppServiceDataData, }