#![deny(missing_copy_implementations,
trivial_casts,
trivial_numeric_casts,
unsafe_code,
unused_import_braces,
unused_qualifications)]
#![warn(missing_docs)]
#[cfg(all(unix, not(target_os = "macos")))]
extern crate dbus;
#[cfg(all(feature = "images", unix, not(target_os = "macos")))] extern crate image;
#[cfg(all(feature = "images", unix, not(target_os = "macos")))] use image::GenericImageView;
#[cfg(all(feature = "images", unix, not(target_os = "macos")))] use std::path::Path;
#[cfg(target_os = "macos")] extern crate mac_notification_sys;
#[cfg(target_os = "macos")] pub use mac_notification_sys::{get_bundle_identifier_or_default, set_application};
#[cfg(all(unix, not(target_os = "macos")))] use dbus::{Connection, BusType, MessageItem, MessageItemArray};
#[cfg(all(unix, not(target_os = "macos")))] mod util;
#[cfg(all(unix, not(target_os = "macos")))] pub mod server;
#[cfg(target_os = "macos")] mod macos;
#[cfg(target_os = "macos")] pub use macos::*;
#[cfg(all(unix, not(target_os = "macos")))] mod xdg;
#[cfg(all(unix, not(target_os = "macos")))] pub use xdg::NotificationHandle;
#[cfg(all(unix, not(target_os = "macos")))] pub use xdg::{ get_capabilities, get_server_information, handle_actions, stop_server };
#[cfg(all(unix, not(target_os = "macos")))] use xdg::build_message;
#[macro_use]
extern crate error_chain;
#[macro_use]
#[cfg(all(feature = "images", unix, not(target_os = "macos")))]
extern crate lazy_static;
pub mod hints;
pub use hints::NotificationHint;
#[cfg(feature = "images")]
pub use hints::NotificationImage;
pub mod error;
pub use error::Error;
use error::*;
mod miniver;
use std::collections::HashSet;
use std::default::Default;
use std::env;
#[cfg(feature = "images")]
lazy_static!{
pub static ref SPEC_VERSION: miniver::Version =
get_server_information()
.and_then(|info| info.spec_version.parse::<miniver::Version>())
.unwrap_or(miniver::Version::new(1,1));
}
#[derive(Debug, Clone)]
pub struct Notification {
pub appname: String,
pub summary: String,
pub subtitle: Option<String>,
pub body: String,
pub icon: String,
pub hints: HashSet<NotificationHint>,
pub actions: Vec<String>,
#[cfg(target_os="macos")] sound_name: Option<String>,
pub timeout: Timeout, id: Option<u32>
}
impl Notification {
pub fn new() -> Notification {
Notification::default()
}
pub fn appname(&mut self, appname: &str) -> &mut Notification {
self.appname = appname.to_owned();
self
}
pub fn summary(&mut self, summary: &str) -> &mut Notification {
self.summary = summary.to_owned();
self
}
pub fn subtitle(&mut self, subtitle: &str) -> &mut Notification {
self.subtitle = Some(subtitle.to_owned());
self
}
#[cfg(all(feature = "images", unix, not(target_os = "macos")))]
pub fn image_data(&mut self, image: NotificationImage) -> &mut Notification {
self.hint(NotificationHint::ImageData(image));
self
}
#[cfg(all(unix, not(target_os = "macos")))]
pub fn image_path(&mut self, path: &str) -> &mut Notification {
self.hint(NotificationHint::ImagePath(path.to_string()));
self
}
#[cfg(all(feature = "images", unix, not(target_os = "macos")))]
pub fn image<T: AsRef<Path> + Sized>(&mut self, path: T) -> &mut Notification {
if let Ok(img) = image::open(&path) {
if let Some(image_data) = img.as_rgb8() {
let (width, height) = img.dimensions();
let image_data = image_data.clone().into_raw();
self.hint(
NotificationHint::ImageData(
NotificationImage::from_rgb(
width as i32,
height as i32,
image_data
).unwrap()
)
);
}
} else {
println!("notify-rust: could not open image {}", path.as_ref().display())
}
self
}
#[cfg(all(unix, not(target_os = "macos")))]
pub fn sound_name(&mut self, name: &str) -> &mut Notification {
self.hint(NotificationHint::SoundName(name.to_owned()));
self
}
#[cfg(target_os = "macos")]
pub fn sound_name(&mut self, name: &str) -> &mut Notification {
self.sound_name = Some(name.to_owned());
self
}
pub fn body(&mut self, body: &str) -> &mut Notification {
self.body = body.to_owned();
self
}
pub fn icon(&mut self, icon: &str) -> &mut Notification {
self.icon = icon.to_owned();
self
}
pub fn auto_icon(&mut self) -> &mut Notification {
self.icon = exe_name();
self
}
pub fn hint(&mut self, hint: NotificationHint) -> &mut Notification {
self.hints.insert(hint);
self
}
pub fn timeout<T: Into<Timeout>>(&mut self, timeout: T) -> &mut Notification {
self.timeout = timeout.into();
self
}
pub fn urgency(&mut self, urgency: NotificationUrgency) -> &mut Notification {
self.hint(NotificationHint::Urgency(urgency)); self
}
#[deprecated(note = "please use .action() only")]
pub fn actions(&mut self, actions: Vec<String>) -> &mut Notification {
self.actions = actions;
self
}
pub fn action(&mut self, identifier: &str, label: &str) -> &mut Notification {
self.actions.push(identifier.to_owned());
self.actions.push(label.to_owned());
self
}
pub fn id(&mut self, id: u32) -> &mut Notification {
self.id = Some(id);
self
}
pub fn finalize(&self) -> Notification {
self.clone()
}
#[cfg(all(unix, not(target_os = "macos")))]
fn pack_hints(&self) -> Result<MessageItem> {
if !self.hints.is_empty() {
let hints = self.hints.iter().map(|hint| hint.into()).collect::<Vec<_>>();
if let Ok(array) = MessageItem::new_array(hints) {
return Ok(array);
}
}
Ok(MessageItem::Array(MessageItemArray::new(vec![], "a{sv}".into()).unwrap()))
}
#[cfg(all(unix, not(target_os = "macos")))]
fn pack_actions(&self) -> MessageItem {
if !self.actions.is_empty() {
let mut actions = vec![];
for action in &self.actions {
actions.push(action.to_owned().into());
}
if let Ok(array) = MessageItem::new_array(actions) {
return array;
}
}
MessageItem::Array(MessageItemArray::new(vec![], "as".into()).unwrap())
}
#[cfg(all(unix, not(target_os = "macos")))]
pub fn show(&self) -> Result<NotificationHandle> {
let connection = Connection::get_private(BusType::Session)?;
let inner_id = self.id.unwrap_or(0);
let id = self._show(inner_id, &connection)?;
Ok(NotificationHandle::new(id, connection, self.clone()))
}
#[cfg(target_os = "macos")]
pub fn show(&self) -> Result<NotificationHandle> {
Ok(mac_notification_sys::send_notification(
&self.summary, &self.subtitle.as_ref().map(|s| &**s), &self.body, &self.sound_name.as_ref().map(|s| &**s) ).map(|_| NotificationHandle::new(self.clone()))?)
}
#[cfg(all(unix, not(target_os = "macos")))]
fn _show(&self, id: u32, connection: &Connection) -> Result<u32> {
let mut message = build_message("Notify");
let timeout: i32 = self.timeout.into();
message.append_items(&[self.appname.to_owned().into(), id.into(), self.icon.to_owned().into(), self.summary.to_owned().into(), self.body.to_owned().into(), self.pack_actions(), self.pack_hints()?, timeout.into() ]);
let reply = connection.send_with_reply_and_block(message, 2000)?;
match reply.get_items().get(0) {
Some(&MessageItem::UInt32(ref id)) => Ok(*id),
_ => Ok(0)
}
}
#[cfg(all(unix, not(target_os = "macos")))]
pub fn show_debug(&mut self) -> Result<NotificationHandle> {
println!("Notification:\n{appname}: ({icon}) {summary:?} {body:?}\nhints: [{hints:?}]\n",
appname = self.appname,
summary = self.summary,
body = self.body,
hints = self.hints,
icon = self.icon,);
self.show()
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum Timeout {
Default,
Never,
Milliseconds(u32)
}
impl Default for Timeout {
fn default() -> Self {
Timeout::Default
}
}
impl From<i32> for Timeout {
fn from(int: i32) -> Timeout {
if int < 0 { Timeout::Default }
else if int == 0 { Timeout::Never }
else { Timeout::Milliseconds(int as u32) }
}
}
impl Into<i32> for Timeout {
fn into(self) -> i32 {
match self {
Timeout::Default => -1,
Timeout::Never => 0,
Timeout::Milliseconds(ms) => ms as i32
}
}
}
#[cfg(all(unix, not(target_os = "macos")))]
impl<'a> dbus::FromMessageItem<'a> for Timeout {
fn from(i: &'a MessageItem) -> std::result::Result<Timeout, ()> {
if let MessageItem::Int32(ref b) = *i {
let timeout_millis: i32 = *b;
Ok(timeout_millis.into())
} else {
Err(())
}
}
}
impl Default for Notification {
#[cfg(all(unix, not(target_os = "macos")))]
fn default() -> Notification {
Notification {
appname: exe_name(),
summary: String::new(),
subtitle: None,
body: String::new(),
icon: String::new(),
hints: HashSet::new(),
actions: Vec::new(),
timeout: Timeout::Default,
id: None
}
}
#[cfg(target_os = "macos")]
fn default() -> Notification {
Notification {
appname: exe_name(),
summary: String::new(),
subtitle: None,
body: String::new(),
icon: String::new(),
hints: HashSet::new(),
actions: Vec::new(),
timeout: Timeout::Default,
sound_name: Default::default(),
id: None
}
}
}
#[derive(Eq, PartialEq, Hash, Copy, Clone, Debug)]
pub enum NotificationUrgency {
Low = 0,
Normal = 1,
Critical = 2
}
impl<'a> From<&'a str> for NotificationUrgency {
fn from(string: &'a str) -> NotificationUrgency {
match string.to_lowercase().as_ref() {
"low" |
"lo" => NotificationUrgency::Low,
"normal" |
"medium" => NotificationUrgency::Normal,
"critical" |
"high" |
"hi" => NotificationUrgency::Critical,
_ => unimplemented!()
}
}
}
impl From<Option<u64>> for NotificationUrgency {
fn from(maybe_int: Option<u64>) -> NotificationUrgency {
match maybe_int {
Some(0) => NotificationUrgency::Low,
Some(2) => NotificationUrgency::Critical,
_ => NotificationUrgency::Normal
}
}
}
#[derive(Debug)]
pub struct ServerInformation {
pub name: String,
pub vendor: String,
pub version: String,
pub spec_version: String
}
fn exe_name() -> String {
env::current_exe().unwrap()
.file_name().unwrap().to_str().unwrap().to_owned()
}