extern crate dbus;
use std::rc::Rc;
use dbus::{arg, BusType, Connection, Message};
use super::DBusError;
use player::{MPRIS2_PATH, MPRIS2_PREFIX, Player, DEFAULT_TIMEOUT_MS};
use pooled_connection::PooledConnection;
const LIST_NAMES_TIMEOUT_MS: i32 = 500;
#[derive(Fail, Debug)]
pub enum FindingError {
#[fail(display = "No player found")]
NoPlayerFound,
#[fail(display = "{}", _0)]
DBusError(#[cause] DBusError),
}
impl From<dbus::Error> for FindingError {
fn from(error: dbus::Error) -> Self {
FindingError::DBusError(error.into())
}
}
impl From<DBusError> for FindingError {
fn from(error: DBusError) -> Self {
FindingError::DBusError(error)
}
}
#[derive(Debug)]
pub struct PlayerFinder {
connection: Rc<PooledConnection>,
}
impl PlayerFinder {
pub fn new() -> Result<Self, DBusError> {
Ok(PlayerFinder::for_connection(Connection::get_private(
BusType::Session,
)?))
}
pub fn for_connection(connection: Connection) -> Self {
PlayerFinder {
connection: Rc::new(connection.into()),
}
}
pub fn find_all<'a>(&self) -> Result<Vec<Player<'a>>, FindingError> {
self.all_player_buses()
.map_err(FindingError::from)?
.into_iter()
.map(|bus_name| {
Player::for_pooled_connection(
Rc::clone(&self.connection),
bus_name.into(),
MPRIS2_PATH.into(),
DEFAULT_TIMEOUT_MS,
).map_err(FindingError::from)
})
.collect()
}
pub fn find_active<'a>(&self) -> Result<Player<'a>, FindingError> {
if let Some(bus_name) = self.active_player_bus()? {
Player::for_pooled_connection(
Rc::clone(&self.connection),
bus_name.into(),
MPRIS2_PATH.into(),
DEFAULT_TIMEOUT_MS,
).map_err(FindingError::from)
} else {
Err(FindingError::NoPlayerFound)
}
}
fn active_player_bus(&self) -> Result<Option<String>, FindingError> {
Ok(self.all_player_buses()?.into_iter().nth(0))
}
fn all_player_buses(&self) -> Result<Vec<String>, DBusError> {
let list_names = Message::new_method_call(
"org.freedesktop.DBus",
"/",
"org.freedesktop.DBus",
"ListNames",
).unwrap();
let reply = self.connection
.underlying()
.send_with_reply_and_block(list_names, LIST_NAMES_TIMEOUT_MS)?;
let names: arg::Array<&str, _> = reply
.get1()
.ok_or_else(|| DBusError::new("Could not get ListNames reply"))?;
Ok(names
.into_iter()
.filter(|name| name.starts_with(MPRIS2_PREFIX))
.map(|str_ref| str_ref.to_owned())
.collect())
}
}