1extern crate notify_rust;
2
3use std::collections::HashMap;
4use notify_rust::Notification;
5
6const BREAK_TAG: &'static str = "!break!";
7
8const VALID_TAGS: [&'static str; 11] = ["status", "url", "file", "artist", "album", "discnumber", "tracknumber", "title", "date", "duration", BREAK_TAG];
9
10pub type Metadata = HashMap<String, String>;
11
12pub fn print_usage() {
13 println!("You must set cmus to call this script as notifier");
14}
15
16pub fn parse(cmus_data: String) -> Metadata {
17 let mut result = Metadata::new();
18 let mut split_data: Vec<&str> = cmus_data.split(" ").collect();
19 split_data.push(BREAK_TAG);
20 let mut last_tag_found: Option<&str> = None;
21 let mut value_collector: Vec<&str> = vec![];
22 for part in split_data.iter() {
23 if VALID_TAGS.contains(part) {
24 if value_collector.len() > 0 {
25 if let Some(tag) = last_tag_found {
26 result.insert(tag.to_string(), value_collector.join(" "));
27 value_collector.clear();
28 }
29 }
30 last_tag_found = Some(part);
31 continue;
32 }
33 value_collector.push(part);
34 }
35 result
36}
37
38pub fn format_notification_body(m: &Metadata) -> String {
39 let mut notification_body = match m.get("title") {
40 Some(t) => t.to_string(),
41 None => "Unknown".to_string()
42 };
43
44 if let Some(artist) = m.get("artist") {
45 notification_body = format!("{} by {}", notification_body, artist);
46 if let Some(album) = m.get("album") {
47 notification_body = format!("{}, {}", notification_body, album);
48 }
49 }
50
51 notification_body
52}
53
54pub trait Notifier {
55 fn send(&self, summary: String, content: String);
56}
57
58pub struct DbusNotifier { }
59
60impl Notifier for DbusNotifier {
61 fn send(&self, summary: String, content: String) {
62 Notification::new()
63 .summary(&summary)
64 .body(&content)
65 .show().unwrap();
66 }
67}
68
69pub fn run<T>(n: &T, cmus_data: String)
70 where T: Notifier {
71 let metadata = parse(cmus_data);
72 let notification_body = format_notification_body(&metadata);
73 n.send("Cmustify - Current song".to_string(), notification_body);
74}
75
76
77#[cfg(test)]
78mod tests {
79 use super::*;
80 use std::cell::RefCell;
81
82 #[test]
83 fn parse_cmus_data_correctly() {
84 let cmus_data = "artist Todd album Reno title super song".to_string();
85 let result = parse(cmus_data);
86
87 assert_eq!(result.get("artist").unwrap(), "Todd");
88 assert_eq!(result.get("album").unwrap(), "Reno");
89 assert_eq!(result.get("title").unwrap(), "super song");
90 }
91
92 #[test]
93 fn format_notification_body_correctly() {
94 let mut metadata = Metadata::new();
95 metadata.insert("title".to_string(), "Super title".to_string());
96 metadata.insert("artist".to_string(), "Todd".to_string());
97
98 let notification_body = format_notification_body(&metadata);
99
100 assert!(notification_body.contains("Super title"));
101 assert!(notification_body.contains("Todd"));
102 }
103
104 #[test]
105 fn run_send_notification() {
106 struct NotifyMock {
107 called: RefCell<bool>
108 }
109
110 impl Notifier for NotifyMock {
111 fn send(&self, _summary: String, _content: String) {
112 *self.called.borrow_mut() = true;
113 }
114 }
115
116 let notifier_mock = NotifyMock { called: RefCell::new(false) };
117 let notification_body = "Hola".to_string();
118 run(¬ifier_mock, notification_body);
119
120 assert!(*notifier_mock.called.borrow())
121 }
122}