cmus_status/
lib.rs

1//! A crate for serialising cmus status program text outputs for use in programs like status bars
2//! and Discord RPC programs.
3#[cfg(test)]
4mod tests;
5
6/// Different status types for the structure.
7#[derive(Debug, Clone, PartialEq)]
8pub enum STATUSTYPE {
9    /// File is playing.
10    PLAYING,
11    /// File is paused.
12    PAUSED,
13    /// Player is stopped.
14    STOPPED,
15    /// Player is exiting.
16    EXITING,
17    /// Unknown status.
18    UNDEFINED,
19}
20
21/// Structured cmus status data.
22#[derive(Debug, Clone, PartialEq)]
23pub struct Status {
24    /// Status type.
25    pub status: STATUSTYPE,
26    /// File location.
27    pub file: Option<String>,
28    /// Track artist name.
29    pub artist: Option<String>,
30    /// Album artist name.
31    pub albumartist: Option<String>,
32    /// Album name.
33    pub album: Option<String>,
34    /// Disc number.
35    pub discnumber: Option<u32>,
36    /// Track number.
37    pub tracknumber: Option<u32>,
38    /// Song title.
39    pub title: Option<String>,
40    /// Album release year.
41    pub date: Option<u32>,
42    /// Duration of song in seconds.
43    pub duration: Option<u32>,
44}
45
46impl Status {
47    /// Creates and returns an empty `Status` object.
48    pub fn new() -> Status {
49        return Status { status: STATUSTYPE::UNDEFINED, file: None, artist: None, albumartist: None, album: None, discnumber: None, tracknumber: None, title: None, date: None, duration: None };
50    }
51}
52
53/// Takes in raw cmus status data from a status program and turns it into structured data for use
54/// in Rust programs.
55pub fn serialise(status: &str) -> Status {
56    let mut modstatus = status.to_string();
57    modstatus = modstatus.replace("status ", ";;st:");
58    modstatus = modstatus.replace("file ", ";;fi:");
59    modstatus = modstatus.replace("albumartist ", ";;ab:");
60    modstatus = modstatus.replace("artist ", ";;ar:");
61    modstatus = modstatus.replace("album ", ";;al:");
62    modstatus = modstatus.replace("discnumber ", ";;dn:");
63    modstatus = modstatus.replace("tracknumber ", ";;tn:");
64    modstatus = modstatus.replace("title ", ";;ti:");
65    modstatus = modstatus.replace("date ", ";;da:");
66    modstatus = modstatus.replace("duration ", ";;du:");
67
68    let segments: Vec<String> = modstatus.split_whitespace().map(|x| x.to_string()).collect();
69
70
71    let mut status = Status::new();
72    let mut multi_line_file_name = false;
73    let mut segtype_multi = "".to_string();
74    let mut multi_line_scratch = "".to_string();
75    let mut i = 0;
76    loop {
77        let x;
78        if i >= segments.len() {
79            x = "".to_string();
80        } else {
81            x = segments[i].clone();
82        }
83
84        if !multi_line_file_name && x.starts_with(";;") {
85            let (first, last) = x.split_at(5);
86            let segtype = first.replace(";;", "").replace(":", "");
87
88            if segtype == "st" {
89                match last {
90                    "playing" => status.status = STATUSTYPE::PLAYING,
91                    "paused" => status.status = STATUSTYPE::PAUSED,
92                    "stopped" => status.status = STATUSTYPE::STOPPED,
93                    "exiting" => status.status = STATUSTYPE::EXITING,
94                    _ => status.status = STATUSTYPE::UNDEFINED,
95                }
96            } else if segtype == "fi" || segtype == "ar" || segtype == "ab" || segtype == "al" || segtype == "ti" {
97                multi_line_scratch = last.to_string();
98                multi_line_file_name = true;
99                segtype_multi = segtype.to_string();
100            } else if segtype == "dn" {
101                let parsed = last.parse::<u32>();
102
103                if parsed.is_ok() {
104                    status.discnumber = Some(parsed.unwrap().clone());
105                }
106            } else if segtype == "tn" {
107                let parsed = last.parse::<u32>();
108
109                if parsed.is_ok() {
110                    status.tracknumber = Some(parsed.unwrap().clone());
111                }
112            } else if segtype == "da" {
113                let parsed = last.parse::<u32>();
114
115                if parsed.is_ok() {
116                    status.date = Some(parsed.unwrap().clone());
117                }
118            } else if segtype == "du" {
119                let parsed = last.parse::<u32>();
120
121                if parsed.is_ok() {
122                    status.duration = Some(parsed.unwrap().clone());
123                }
124            }
125            i = i + 1;
126            if i >= segments.len() {
127                break;
128            }
129        } else if multi_line_file_name {
130            if !x.starts_with(";;") && x != "".to_string() {
131                multi_line_scratch = format!("{} {}", multi_line_scratch, x.to_string());
132                i = i + 1;
133            } else {
134                multi_line_file_name = false;
135                match segtype_multi.as_str() {
136                    "fi" => status.file = Some(multi_line_scratch.clone()),
137                    "ar" => status.artist = Some(multi_line_scratch.clone()),
138                    "ab" => status.albumartist = Some(multi_line_scratch.clone()),
139                    "al" => status.album = Some(multi_line_scratch.clone()),
140                    "ti" => status.title = Some(multi_line_scratch.clone()),
141                    _ => panic!(),
142                }
143            }
144        } else {
145            i = i + 1;
146        }
147    }
148
149    return status;
150}
151