Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: better error handling #29

Merged
merged 8 commits into from
May 30, 2024
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/target/
**/*.rs.bk
Cargo.lock
/.idea
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ default-target = "x86_64-pc-windows-msvc"

[dependencies]
quick-xml = "0.31"
thiserror = "1.0.61"
windows-version = "0.1"

[dependencies.windows]
Expand Down
2 changes: 1 addition & 1 deletion examples/simple.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ fn main() {
.text1("(╯°□°)╯︵ ┻━┻")
.sound(Some(Sound::SMS))
.duration(Duration::Short)
.on_activated(move |action| -> windows::core::Result<()> {
.on_activated(move |action| {
match action {
Some(action) => println!("You've clicked {}!", action),
None => println!("You've clicked me!"),
Expand Down
67 changes: 41 additions & 26 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,23 @@ use std::fmt::Write;
use std::path::Path;
use std::str::FromStr;

pub use windows::core::{Error, Result, HSTRING};
pub use windows::core::HSTRING;
pub use windows::UI::Notifications::ToastNotification;

use thiserror::Error;

#[derive(Error, Debug)]
pub enum ToastError {
#[error("Windows API error: {0}")]
Os(#[from] windows::core::Error),
#[error("IO error: {0}")]
Io(#[from] std::io::Error),
#[error("Unknown error")]
Unknown,
}

pub type Result<T> = std::result::Result<T, ToastError>;

/// `ToastDismissalReason` is a struct representing the reason a toast notification was dismissed.
///
/// Variants:
Expand Down Expand Up @@ -255,7 +269,7 @@ pub enum Scenario {
Default,
/// This will be displayed pre-expanded and stay on the user's screen till dismissed. Audio will loop by default and will use alarm audio.
Alarm,
/// This will be displayed pre-expanded and stay on the user's screen till dismissed..
/// This will be displayed pre-expanded and stay on the user's screen till dismissed.
Reminder,
/// This will be displayed pre-expanded in a special call format and stay on the user's screen till dismissed. Audio will loop by default and will use ringtone audio.
IncomingCall,
Expand All @@ -264,7 +278,7 @@ pub enum Scenario {
impl Toast {
/// This can be used if you do not have a AppUserModelID.
///
/// However, the toast will erroniously report its origin as powershell.
/// However, the toast will erroneously report its origin as powershell.
pub const POWERSHELL_APP_ID: &'static str = "{1AC14E77-02E7-4E5D-B744-2EB1AE5198B7}\
\\WindowsPowerShell\\v1.0\\powershell.exe";
/// Constructor for the toast builder.
Expand Down Expand Up @@ -372,7 +386,7 @@ impl Toast {
);
self
} else {
// Win81 rejects the above xml so we fallback to a simpler call
// Win81 rejects the above xml, so we fall back to a simpler call
self.image(source, alt_text)
}
}
Expand All @@ -390,7 +404,7 @@ impl Toast {
);
self
} else {
// win81 rejects the above xml so we fallback to a simpler call
// win81 rejects the above xml, so we fall back to a simpler call
self.image(source, alt_text)
}
}
Expand Down Expand Up @@ -436,7 +450,7 @@ impl Toast {

/// Adds a button to the notification
/// `content` is the text of the button.
/// `action` will be send as an argument [on_activated](Self::on_activated) when the button is clicked.
/// `action` will be sent as an argument [on_activated](Self::on_activated) when the button is clicked.
pub fn add_button(mut self, content: &str, action: &str) -> Toast {
self.buttons.push(Button {
content: content.to_owned(),
Expand All @@ -447,12 +461,13 @@ impl Toast {

// HACK: f is static so that we know the function is valid to call.
// this would be nice to remove at some point
pub fn on_activated<F: FnMut(Option<String>) -> Result<()> + Send + 'static>(
mut self,
mut f: F,
) -> Self {
pub fn on_activated<F>(mut self, mut f: F) -> Self
where
F: FnMut(Option<String>) + Send + 'static,
{
self.on_activated = Some(TypedEventHandler::new(move |_, insp| {
f(Self::get_activated_action(insp))
f(Self::get_activated_action(insp));
Ok(())
}));
self
}
Expand Down Expand Up @@ -489,15 +504,15 @@ impl Toast {
/// Some(ToastDismissalReason::TimedOut) => println!("TimedOut"),
/// _ => println!("Unknown"),
/// }
/// Ok(())
/// }).show().expect("notification failed");
/// ```
pub fn on_dismissed<F: Fn(Option<ToastDismissalReason>) -> Result<()> + Send + 'static>(
pub fn on_dismissed<F: Fn(Option<ToastDismissalReason>) + Send + 'static>(
mut self,
f: F,
) -> Self {
self.on_dismissed = Some(TypedEventHandler::new(move |_, args| {
f(Self::get_dismissed_reason(args))
f(Self::get_dismissed_reason(args));
Ok(())
}));
self
}
Expand All @@ -513,7 +528,7 @@ impl Toast {
None
}

fn create_template(&self) -> windows::core::Result<ToastNotification> {
fn create_template(&self) -> Result<ToastNotification> {
//using this to get an instance of XmlDocument
let toast_xml = XmlDocument::new()?;

Expand Down Expand Up @@ -542,16 +557,16 @@ impl Toast {
}

toast_xml.LoadXml(&HSTRING::from(format!(
"<toast {} {}>
<visual>
<binding template=\"{}\">
r#"<toast {} {}>
<visual>
<binding template="{}">
{}
{}{}{}
</binding>
</visual>
{}
{}
</toast>",
</binding>
</visual>
{}
{}
</toast>"#,
self.duration,
self.scenario,
template_binding,
Expand All @@ -564,11 +579,11 @@ impl Toast {
)))?;

// Create the toast
ToastNotification::CreateToastNotification(&toast_xml)
Ok(ToastNotification::CreateToastNotification(&toast_xml)?)
}

/// Display the toast on the screen
pub fn show(&self) -> windows::core::Result<()> {
pub fn show(&self) -> Result<()> {
let toast_template = self.create_template()?;
if let Some(handler) = &self.on_activated {
toast_template.Activated(handler)?;
Expand All @@ -582,7 +597,7 @@ impl Toast {
ToastNotificationManager::CreateToastNotifierWithId(&HSTRING::from(&self.app_id))?;

// Show the toast.
let result = toast_notifier.Show(&toast_template);
let result = Ok(toast_notifier.Show(&toast_template)?);
std::thread::sleep(std::time::Duration::from_millis(10));
result
}
Expand Down