Krolyxon
Commit 61cb83e

rmptuisrcconfigmpd_conf.rs

87 lines · 2.5 KB · Rust

1//! Reading `music_directory` from the local MPD configuration.
2//!
3//! MPD only reveals its music directory over a Unix socket, so for a local
4//! server reached over TCP rmptui reads it from MPD's own config file.
5
6use std::{
7 env, fs,
8 path::{Path, PathBuf},
9};
10
11use crate::{mpd::Address, util::expand_tilde};
12
13/// The music directory of a local MPD, if `address` is local and an MPD
14/// config file names one.
15pub fn local_music_directory(address: &Address) -> Option<PathBuf> {
16 if !address.is_local() {
17 return None;
18 }
19 candidates()
20 .iter()
21 .find_map(|path| fs::read_to_string(path).ok())
22 .and_then(|contents| parse_music_directory(&contents))
23}
24
25/// MPD's config file locations, in MPD's lookup order.
26fn candidates() -> Vec<PathBuf> {
27 let mut paths = Vec::new();
28 if let Some(dir) = env::var_os("XDG_CONFIG_HOME").filter(|d| !d.is_empty()) {
29 paths.push(Path::new(&dir).join("mpd/mpd.conf"));
30 }
31 paths.extend(
32 [
33 "~/.config/mpd/mpd.conf",
34 "~/.mpdconf",
35 "~/.mpd/mpd.conf",
36 "/etc/mpd.conf",
37 ]
38 .map(expand_tilde),
39 );
40 paths
41}
42
43fn parse_music_directory(contents: &str) -> Option<PathBuf> {
44 contents.lines().find_map(|line| {
45 let line = line.trim();
46 let value = line.strip_prefix("music_directory")?;
47 if !value.starts_with(char::is_whitespace) {
48 return None;
49 }
50 let value = value.trim();
51 let value = value
52 .strip_prefix('"')
53 .and_then(|v| v.split_once('"'))
54 .map_or(value, |(inner, _)| inner);
55 (!value.is_empty()).then(|| expand_tilde(value))
56 })
57}
58
59#[cfg(test)]
60mod tests {
61 use super::*;
62
63 #[test]
64 fn parses_music_directory() {
65 let conf = "# music_directory \"/commented\"\n\
66 music_directory_x \"/no\"\n\
67 music_directory\t\t\"~/music\" # trailing comment\n";
68 let home = env::var("HOME").unwrap();
69 assert_eq!(
70 parse_music_directory(conf),
71 Some(PathBuf::from(format!("{home}/music")))
72 );
73 assert_eq!(
74 parse_music_directory("music_directory /srv/music"),
75 Some(PathBuf::from("/srv/music"))
76 );
77 assert_eq!(parse_music_directory("port \"6600\""), None);
78 }
79
80 #[test]
81 fn ignores_remote_servers() {
82 assert_eq!(
83 local_music_directory(&Address::Tcp("192.168.1.20:6600".into())),
84 None
85 );
86 }
87}