cmustify/
lib.rs

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(&notifier_mock, notification_body);
119
120        assert!(*notifier_mock.called.borrow())
121    }
122}