#![deny(
missing_copy_implementations,
trivial_casts, trivial_numeric_casts,
unsafe_code,
unstable_features,
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::GenericImage;
#[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 std::borrow::Cow;
#[cfg(all(unix, not(target_os = "macos")))] use dbus::{Connection, BusType, MessageItem};
#[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")))] 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;
use error::*;
pub use error::Error;
mod miniver;
use std::env;
use std::collections::HashSet;
use std::default::Default;
#[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);
}
}
let sig = Cow::Borrowed("{sv}");
Ok(MessageItem::Array(vec![], sig))
}
#[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;
}
}
let sig = Cow::Borrowed("s"); MessageItem::Array(vec![], sig)
}
#[cfg(all(unix, not(target_os = "macos")))]
pub fn show(&self) -> Result<NotificationHandle> {
let connection = try!(Connection::get_private(BusType::Session));
let inner_id = self.id.unwrap_or(0);
let id = try!(self._show(inner_id, &connection));
Ok(NotificationHandle::new(id, connection, self.clone()))
}
#[cfg(target_os = "macos")]
pub fn show(&self) -> std::result::Result<NotificationHandle, mac_notification_sys::error::ErrorKind> {
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().into(), self.pack_hints()?.into(), timeout.into() ]);
let reply = try!(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 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!()
}
}
}
#[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()
}