extern crate tempdir;
use self::tempdir::TempDir;
use super::mpd;
use std::fs::{create_dir, File};
use std::io::{Read, Write};
use std::os::unix::net::UnixStream;
use std::path::{Path, PathBuf};
use std::process::{Child, Command, Stdio};
struct MpdConfig {
db_file: PathBuf,
music_directory: PathBuf,
playlist_directory: PathBuf,
sticker_file: PathBuf,
config_path: PathBuf,
sock_path: PathBuf,
}
impl MpdConfig {
pub fn new
(base: P) -> MpdConfig
where P: AsRef {
let base = base.as_ref();
MpdConfig {
db_file: base.join("db"),
music_directory: base.join("music"),
playlist_directory: base.join("playlists"),
sticker_file: base.join("sticker_file"),
config_path: base.join("config"),
sock_path: base.join("sock"),
}
}
fn config_text(&self) -> String {
format!(
r#"
db_file "{db_file}"
log_file "/dev/null"
music_directory "{music_directory}"
playlist_directory "{playlist_directory}"
sticker_file "{sticker_file}"
bind_to_address "{sock_path}"
audio_output {{
type "null"
name "null"
}}
"#,
db_file = self.db_file.display(),
music_directory = self.music_directory.display(),
playlist_directory = self.playlist_directory.display(),
sticker_file = self.sticker_file.display(),
sock_path = self.sock_path.display(),
)
}
fn generate(&self) {
create_dir(&self.music_directory).expect("Could not create music directory.");
create_dir(&self.playlist_directory).expect("Could not create playlist directory.");
let mut file = File::create(&self.config_path).expect("Could not create config file.");
file.write_all(self.config_text().as_bytes()).expect("Could not write config file.");
}
}
pub struct Daemon {
// Saved here so it gets dropped when this does.
_temp_dir: TempDir,
config: MpdConfig,
process: Child,
}
impl Drop for Daemon {
fn drop(&mut self) {
self.process.kill().expect("Could not kill mpd daemon.");
self.process.wait().expect("Could not wait for mpd daemon to shutdown.");
if let Some(ref mut stderr) = self.process.stderr {
let mut output = String::new();
stderr.read_to_string(&mut output).expect("Could not collect output from mpd.");
println! {"Output from mpd:"}
println! {"{}", output};
}
}
}
fn sleep() {
use std::{thread, time};
let ten_millis = time::Duration::from_millis(10);
thread::sleep(ten_millis);
}
static EMPTY_FLAC_BYTES: &'static [u8] = include_bytes!("../data/empty.flac");
impl Daemon {
pub fn start() -> Daemon {
let temp_dir = TempDir::new("mpd-test").unwrap();
let config = MpdConfig::new(&temp_dir);
config.generate();
// TODO: Factor out putting files in the music directory.
File::create(config.music_directory.join("empty.flac")).unwrap().write_all(EMPTY_FLAC_BYTES).unwrap();
let process = Command::new("mpd")
.arg("--no-daemon")
.arg(&config.config_path)
.stdin(Stdio::null())
.stdout(Stdio::null())
.stderr(Stdio::piped())
.spawn()
.expect("Could not create mpd daemon.");
let daemon = Daemon { _temp_dir: temp_dir, config: config, process: process };
// Wait until we can connect to the daemon
let mut client;
loop {
if let Ok(c) = daemon.maybe_connect() {
client = c;
break;
}
sleep()
}
while let Some(_) = client.status().expect("Couldn't get status.").updating_db {
sleep()
}
daemon
}
fn maybe_connect(&self) -> Result, mpd::error::Error> {
let stream = UnixStream::connect(&self.config.sock_path)?;
mpd::Client::new(stream)
}
pub fn connect(&self) -> mpd::Client {
self.maybe_connect().expect("Could not connect to daemon.")
}
}