Compare commits
3 Commits
ae17032724
...
376936152f
Author | SHA1 | Date |
---|---|---|
Oystein Kristoffer Tveit | 376936152f | |
Oystein Kristoffer Tveit | 3938905f19 | |
Oystein Kristoffer Tveit | 24718c9fc3 |
|
@ -12,8 +12,8 @@ async fn main() -> Result<(), MpvError> {
|
|||
let playlist = mpv.get_playlist().await?;
|
||||
println!("playlist: {:?}", playlist);
|
||||
|
||||
let playback_time: f64 = mpv.get_property("playback-time").await?;
|
||||
println!("playback-time: {}", playback_time);
|
||||
let playback_time: Option<f64> = mpv.get_property("playback-time").await?;
|
||||
println!("playback-time: {:?}", playback_time);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -30,6 +30,7 @@
|
|||
])
|
||||
pkgs.mpv
|
||||
pkgs.grcov
|
||||
pkgs.cargo-nextest
|
||||
];
|
||||
RUST_SRC_PATH = "${toolchain.rust-src}/lib/rustlib/src/rust/";
|
||||
});
|
||||
|
|
|
@ -134,18 +134,22 @@ impl IntoRawCommandPart for SeekOptions {
|
|||
pub trait GetPropertyTypeHandler: Sized {
|
||||
// TODO: fix this
|
||||
#[allow(async_fn_in_trait)]
|
||||
async fn get_property_generic(instance: &Mpv, property: &str) -> Result<Self, MpvError>;
|
||||
async fn get_property_generic(instance: &Mpv, property: &str)
|
||||
-> Result<Option<Self>, MpvError>;
|
||||
}
|
||||
|
||||
impl<T> GetPropertyTypeHandler for T
|
||||
where
|
||||
T: TypeHandler,
|
||||
{
|
||||
async fn get_property_generic(instance: &Mpv, property: &str) -> Result<T, MpvError> {
|
||||
async fn get_property_generic(instance: &Mpv, property: &str) -> Result<Option<T>, MpvError> {
|
||||
instance
|
||||
.get_property_value(property)
|
||||
.await
|
||||
.and_then(T::get_value)
|
||||
.and_then(|value| match value {
|
||||
Some(v) => T::get_value(v).map(|v| Some(v)),
|
||||
None => Ok(None),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -172,7 +176,7 @@ where
|
|||
instance
|
||||
.command_sender
|
||||
.send((
|
||||
MpvIpcCommand::SetProperty(property.to_owned(), value),
|
||||
MpvIpcCommand::SetProperty(property.to_owned(), value.to_owned()),
|
||||
res_tx,
|
||||
))
|
||||
.await
|
||||
|
@ -277,7 +281,7 @@ impl Mpv {
|
|||
command: &str,
|
||||
args: &[&str],
|
||||
) -> Result<Option<Value>, MpvError> {
|
||||
let command = Vec::from(
|
||||
let command_vec = Vec::from(
|
||||
[command]
|
||||
.iter()
|
||||
.chain(args.iter())
|
||||
|
@ -287,7 +291,7 @@ impl Mpv {
|
|||
);
|
||||
let (res_tx, res_rx) = oneshot::channel();
|
||||
self.command_sender
|
||||
.send((MpvIpcCommand::Command(command), res_tx))
|
||||
.send((MpvIpcCommand::Command(command_vec.clone()), res_tx))
|
||||
.await
|
||||
.map_err(|err| MpvError::InternalConnectionError(err.to_string()))?;
|
||||
|
||||
|
@ -462,7 +466,10 @@ impl Mpv {
|
|||
pub async fn get_property<T: GetPropertyTypeHandler>(
|
||||
&self,
|
||||
property: &str,
|
||||
) -> Result<T, MpvError> {
|
||||
) -> Result<Option<T>, MpvError> {
|
||||
// NOTE: `get_property_generic` is implemented in terms of `get_property_value`,
|
||||
// which wraps errors in `MpvError::GetPropertyFailure`. That is why it is
|
||||
// not needed here.
|
||||
T::get_property_generic(self, property).await
|
||||
}
|
||||
|
||||
|
@ -487,7 +494,7 @@ impl Mpv {
|
|||
/// Ok(())
|
||||
/// }
|
||||
/// ```
|
||||
pub async fn get_property_value(&self, property: &str) -> Result<Value, MpvError> {
|
||||
pub async fn get_property_value(&self, property: &str) -> Result<Option<Value>, MpvError> {
|
||||
let (res_tx, res_rx) = oneshot::channel();
|
||||
self.command_sender
|
||||
.send((MpvIpcCommand::GetProperty(property.to_owned()), res_tx))
|
||||
|
@ -495,9 +502,7 @@ impl Mpv {
|
|||
.map_err(|err| MpvError::InternalConnectionError(err.to_string()))?;
|
||||
|
||||
match res_rx.await {
|
||||
Ok(MpvIpcResponse(response)) => {
|
||||
response.and_then(|value| value.ok_or(MpvError::MissingMpvData))
|
||||
}
|
||||
Ok(MpvIpcResponse(response)) => response,
|
||||
Err(err) => Err(MpvError::InternalConnectionError(err.to_string())),
|
||||
}
|
||||
}
|
||||
|
@ -526,11 +531,10 @@ impl Mpv {
|
|||
/// Ok(())
|
||||
/// }
|
||||
/// ```
|
||||
pub async fn set_property<T: SetPropertyTypeHandler<T>>(
|
||||
&self,
|
||||
property: &str,
|
||||
value: T,
|
||||
) -> Result<(), MpvError> {
|
||||
T::set_property_generic(self, property, value).await
|
||||
pub async fn set_property<T>(&self, property: &str, value: T) -> Result<(), MpvError>
|
||||
where
|
||||
T: SetPropertyTypeHandler<T> + Clone + fmt::Debug,
|
||||
{
|
||||
T::set_property_generic(self, property, value.clone()).await
|
||||
}
|
||||
}
|
||||
|
|
45
src/error.rs
45
src/error.rs
|
@ -46,3 +46,48 @@ pub enum MpvError {
|
|||
#[error("Unknown error: {0}")]
|
||||
Other(String),
|
||||
}
|
||||
|
||||
impl PartialEq for MpvError {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
match (self, other) {
|
||||
(Self::MpvError(l0), Self::MpvError(r0)) => l0 == r0,
|
||||
(Self::MpvSocketConnectionError(l0), Self::MpvSocketConnectionError(r0)) => l0 == r0,
|
||||
(Self::InternalConnectionError(l0), Self::InternalConnectionError(r0)) => l0 == r0,
|
||||
(Self::JsonParseError(l0), Self::JsonParseError(r0)) => {
|
||||
l0.to_string() == r0.to_string()
|
||||
}
|
||||
(
|
||||
Self::ValueContainsUnexpectedType {
|
||||
expected_type: l_expected_type,
|
||||
received: l_received,
|
||||
},
|
||||
Self::ValueContainsUnexpectedType {
|
||||
expected_type: r_expected_type,
|
||||
received: r_received,
|
||||
},
|
||||
) => l_expected_type == r_expected_type && l_received == r_received,
|
||||
(
|
||||
Self::DataContainsUnexpectedType {
|
||||
expected_type: l_expected_type,
|
||||
received: l_received,
|
||||
},
|
||||
Self::DataContainsUnexpectedType {
|
||||
expected_type: r_expected_type,
|
||||
received: r_received,
|
||||
},
|
||||
) => l_expected_type == r_expected_type && l_received == r_received,
|
||||
(
|
||||
Self::MissingKeyInObject {
|
||||
key: l_key,
|
||||
map: l_map,
|
||||
},
|
||||
Self::MissingKeyInObject {
|
||||
key: r_key,
|
||||
map: r_map,
|
||||
},
|
||||
) => l_key == r_key && l_map == r_map,
|
||||
|
||||
_ => core::mem::discriminant(self) == core::mem::discriminant(other),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,9 +1,13 @@
|
|||
//! JSON parsing logic for properties returned in [`Event::PropertyChange`]
|
||||
//! JSON parsing logic for properties returned either by
|
||||
//! [`Mpv::get_property`] or [`Event::PropertyChange`]
|
||||
//!
|
||||
//! This module is used to parse the json data from the `data` field of the
|
||||
//! [`Event::PropertyChange`] variant. Mpv has about 1000 different properties
|
||||
//! This module is used to parse the json data from the `data` field of
|
||||
//! known properties. Mpv has about 1000 different properties
|
||||
//! as of `v0.38.0`, so this module will only implement the most common ones.
|
||||
|
||||
// TODO: reuse this logic for providing a more typesafe response API to `Mpv::get_property()`
|
||||
// Although this data is currently of type `Option<`
|
||||
|
||||
use std::collections::HashMap;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
@ -129,14 +133,19 @@ pub fn parse_event_property(event: Event) -> Result<(usize, Property), MpvError>
|
|||
};
|
||||
Ok((id, Property::Metadata(metadata)))
|
||||
}
|
||||
// "playlist" => {
|
||||
// let playlist = match data {
|
||||
// MpvDataType::Array(a) => json_array_to_playlist(&a),
|
||||
// MpvDataType::Null => Vec::new(),
|
||||
// _ => return Err(Error(ErrorCode::ValueDoesNotContainPlaylist)),
|
||||
// };
|
||||
// Ok((id, Property::Playlist(playlist)))
|
||||
// }
|
||||
"playlist" => {
|
||||
let playlist = match data {
|
||||
Some(MpvDataType::Array(a)) => mpv_array_to_playlist(&a)?,
|
||||
None => Vec::new(),
|
||||
Some(data) => {
|
||||
return Err(MpvError::DataContainsUnexpectedType {
|
||||
expected_type: "Array".to_owned(),
|
||||
received: data,
|
||||
})
|
||||
}
|
||||
};
|
||||
Ok((id, Property::Playlist(playlist)))
|
||||
}
|
||||
"playlist-pos" => {
|
||||
let playlist_pos = match data {
|
||||
Some(MpvDataType::Usize(u)) => Some(u),
|
||||
|
@ -246,3 +255,59 @@ pub fn parse_event_property(event: Event) -> Result<(usize, Property), MpvError>
|
|||
_ => Ok((id, Property::Unknown { name, data })),
|
||||
}
|
||||
}
|
||||
|
||||
fn mpv_data_to_playlist_entry(
|
||||
map: &HashMap<String, MpvDataType>,
|
||||
) -> Result<PlaylistEntry, MpvError> {
|
||||
let filename = match map.get("filename") {
|
||||
Some(MpvDataType::String(s)) => s.to_string(),
|
||||
Some(data) => {
|
||||
return Err(MpvError::DataContainsUnexpectedType {
|
||||
expected_type: "String".to_owned(),
|
||||
received: data.clone(),
|
||||
})
|
||||
}
|
||||
None => return Err(MpvError::MissingMpvData),
|
||||
};
|
||||
let title = match map.get("title") {
|
||||
Some(MpvDataType::String(s)) => s.to_string(),
|
||||
Some(data) => {
|
||||
return Err(MpvError::DataContainsUnexpectedType {
|
||||
expected_type: "String".to_owned(),
|
||||
received: data.clone(),
|
||||
})
|
||||
}
|
||||
None => return Err(MpvError::MissingMpvData),
|
||||
};
|
||||
let current = match map.get("current") {
|
||||
Some(MpvDataType::Bool(b)) => *b,
|
||||
Some(data) => {
|
||||
return Err(MpvError::DataContainsUnexpectedType {
|
||||
expected_type: "bool".to_owned(),
|
||||
received: data.clone(),
|
||||
})
|
||||
}
|
||||
None => return Err(MpvError::MissingMpvData),
|
||||
};
|
||||
Ok(PlaylistEntry {
|
||||
id: 0,
|
||||
filename,
|
||||
title,
|
||||
current,
|
||||
})
|
||||
}
|
||||
|
||||
fn mpv_array_to_playlist(array: &[MpvDataType]) -> Result<Vec<PlaylistEntry>, MpvError> {
|
||||
array
|
||||
.iter()
|
||||
.map(|value| match value {
|
||||
MpvDataType::HashMap(map) => mpv_data_to_playlist_entry(map),
|
||||
_ => Err(MpvError::DataContainsUnexpectedType {
|
||||
expected_type: "HashMap".to_owned(),
|
||||
received: value.clone(),
|
||||
}),
|
||||
})
|
||||
.enumerate()
|
||||
.map(|(id, entry)| entry.map(|entry| PlaylistEntry { id, ..entry }))
|
||||
.collect()
|
||||
}
|
||||
|
|
|
@ -141,12 +141,15 @@ pub trait MpvExt {
|
|||
|
||||
impl MpvExt for Mpv {
|
||||
async fn get_metadata(&self) -> Result<HashMap<String, MpvDataType>, MpvError> {
|
||||
self.get_property("metadata").await
|
||||
self.get_property("metadata")
|
||||
.await?
|
||||
.ok_or(MpvError::MissingMpvData)
|
||||
}
|
||||
|
||||
async fn get_playlist(&self) -> Result<Playlist, MpvError> {
|
||||
self.get_property::<Vec<PlaylistEntry>>("playlist")
|
||||
.await
|
||||
.await?
|
||||
.ok_or(MpvError::MissingMpvData)
|
||||
.map(Playlist)
|
||||
}
|
||||
|
||||
|
@ -174,15 +177,15 @@ impl MpvExt for Mpv {
|
|||
let enabled = match option {
|
||||
Switch::On => "yes",
|
||||
Switch::Off => "no",
|
||||
Switch::Toggle => {
|
||||
self.get_property::<String>("pause")
|
||||
.await
|
||||
Switch::Toggle => self
|
||||
.get_property::<String>("pause")
|
||||
.await?
|
||||
.ok_or(MpvError::MissingMpvData)
|
||||
.map(|s| match s.as_str() {
|
||||
"yes" => "no",
|
||||
"no" => "yes",
|
||||
_ => "no",
|
||||
})?
|
||||
}
|
||||
})?,
|
||||
};
|
||||
self.set_property("pause", enabled).await
|
||||
}
|
||||
|
@ -238,17 +241,17 @@ impl MpvExt for Mpv {
|
|||
}
|
||||
|
||||
async fn playlist_play_next(&self, id: usize) -> Result<(), MpvError> {
|
||||
match self.get_property::<usize>("playlist-pos").await {
|
||||
Ok(current_id) => {
|
||||
let current_id = self
|
||||
.get_property::<usize>("playlist-pos")
|
||||
.await?
|
||||
.ok_or(MpvError::MissingMpvData)?;
|
||||
|
||||
self.run_command(MpvCommand::PlaylistMove {
|
||||
from: id,
|
||||
to: current_id + 1,
|
||||
})
|
||||
.await
|
||||
}
|
||||
Err(msg) => Err(msg),
|
||||
}
|
||||
}
|
||||
|
||||
async fn playlist_remove_id(&self, id: usize) -> Result<(), MpvError> {
|
||||
self.run_command(MpvCommand::PlaylistRemove(id)).await
|
||||
|
@ -266,15 +269,15 @@ impl MpvExt for Mpv {
|
|||
let enabled = match option {
|
||||
Switch::On => "inf",
|
||||
Switch::Off => "no",
|
||||
Switch::Toggle => {
|
||||
self.get_property::<String>("loop-file")
|
||||
.await
|
||||
Switch::Toggle => self
|
||||
.get_property::<String>("loop-file")
|
||||
.await?
|
||||
.ok_or(MpvError::MissingMpvData)
|
||||
.map(|s| match s.as_str() {
|
||||
"inf" => "no",
|
||||
"no" => "inf",
|
||||
_ => "no",
|
||||
})?
|
||||
}
|
||||
})?,
|
||||
};
|
||||
self.set_property("loop-file", enabled).await
|
||||
}
|
||||
|
@ -283,32 +286,32 @@ impl MpvExt for Mpv {
|
|||
let enabled = match option {
|
||||
Switch::On => "inf",
|
||||
Switch::Off => "no",
|
||||
Switch::Toggle => {
|
||||
self.get_property::<String>("loop-playlist")
|
||||
.await
|
||||
Switch::Toggle => self
|
||||
.get_property::<String>("loop-playlist")
|
||||
.await?
|
||||
.ok_or(MpvError::MissingMpvData)
|
||||
.map(|s| match s.as_str() {
|
||||
"inf" => "no",
|
||||
"no" => "inf",
|
||||
_ => "no",
|
||||
})?
|
||||
}
|
||||
})?,
|
||||
};
|
||||
self.set_property("loo-playlist", enabled).await
|
||||
self.set_property("loop-playlist", enabled).await
|
||||
}
|
||||
|
||||
async fn set_mute(&self, option: Switch) -> Result<(), MpvError> {
|
||||
let enabled = match option {
|
||||
Switch::On => "yes",
|
||||
Switch::Off => "no",
|
||||
Switch::Toggle => {
|
||||
self.get_property::<String>("mute")
|
||||
.await
|
||||
Switch::Toggle => self
|
||||
.get_property::<String>("mute")
|
||||
.await?
|
||||
.ok_or(MpvError::MissingMpvData)
|
||||
.map(|s| match s.as_str() {
|
||||
"yes" => "no",
|
||||
"no" => "yes",
|
||||
_ => "no",
|
||||
})?
|
||||
}
|
||||
})?,
|
||||
};
|
||||
self.set_property("mute", enabled).await
|
||||
}
|
||||
|
@ -318,19 +321,15 @@ impl MpvExt for Mpv {
|
|||
input_speed: f64,
|
||||
option: NumberChangeOptions,
|
||||
) -> Result<(), MpvError> {
|
||||
match self.get_property::<f64>("speed").await {
|
||||
Ok(speed) => match option {
|
||||
NumberChangeOptions::Increase => {
|
||||
self.set_property("speed", speed + input_speed).await
|
||||
}
|
||||
|
||||
NumberChangeOptions::Decrease => {
|
||||
self.set_property("speed", speed - input_speed).await
|
||||
}
|
||||
let speed = self
|
||||
.get_property::<f64>("speed")
|
||||
.await?
|
||||
.ok_or(MpvError::MissingMpvData)?;
|
||||
|
||||
match option {
|
||||
NumberChangeOptions::Increase => self.set_property("speed", speed + input_speed).await,
|
||||
NumberChangeOptions::Decrease => self.set_property("speed", speed - input_speed).await,
|
||||
NumberChangeOptions::Absolute => self.set_property("speed", input_speed).await,
|
||||
},
|
||||
Err(msg) => Err(msg),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -339,19 +338,19 @@ impl MpvExt for Mpv {
|
|||
input_volume: f64,
|
||||
option: NumberChangeOptions,
|
||||
) -> Result<(), MpvError> {
|
||||
match self.get_property::<f64>("volume").await {
|
||||
Ok(volume) => match option {
|
||||
let volume = self
|
||||
.get_property::<f64>("volume")
|
||||
.await?
|
||||
.ok_or(MpvError::MissingMpvData)?;
|
||||
|
||||
match option {
|
||||
NumberChangeOptions::Increase => {
|
||||
self.set_property("volume", volume + input_volume).await
|
||||
}
|
||||
|
||||
NumberChangeOptions::Decrease => {
|
||||
self.set_property("volume", volume - input_volume).await
|
||||
}
|
||||
|
||||
NumberChangeOptions::Absolute => self.set_property("volume", input_volume).await,
|
||||
},
|
||||
Err(msg) => Err(msg),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -109,7 +109,7 @@ impl TypeHandler for Vec<PlaylistEntry> {
|
|||
expected_type: "Array<Value>".to_string(),
|
||||
received: value.clone(),
|
||||
})
|
||||
.map(|array| json_array_to_playlist(array))
|
||||
.and_then(|array| json_array_to_playlist(array))
|
||||
}
|
||||
|
||||
fn as_string(&self) -> String {
|
||||
|
@ -155,29 +155,65 @@ pub(crate) fn json_array_to_vec(array: &[Value]) -> Result<Vec<MpvDataType>, Mpv
|
|||
array.iter().map(json_to_value).collect()
|
||||
}
|
||||
|
||||
pub(crate) fn json_array_to_playlist(array: &[Value]) -> Vec<PlaylistEntry> {
|
||||
let mut output: Vec<PlaylistEntry> = Vec::new();
|
||||
for (id, entry) in array.iter().enumerate() {
|
||||
let mut filename: String = String::new();
|
||||
let mut title: String = String::new();
|
||||
let mut current: bool = false;
|
||||
if let Value::String(ref f) = entry["filename"] {
|
||||
filename = f.to_string();
|
||||
fn json_map_to_playlist_entry(
|
||||
map: &serde_json::map::Map<String, Value>,
|
||||
) -> Result<PlaylistEntry, MpvError> {
|
||||
let filename = match map.get("filename") {
|
||||
Some(Value::String(s)) => s.to_string(),
|
||||
Some(data) => {
|
||||
return Err(MpvError::ValueContainsUnexpectedType {
|
||||
expected_type: "String".to_owned(),
|
||||
received: data.clone(),
|
||||
})
|
||||
}
|
||||
if let Value::String(ref t) = entry["title"] {
|
||||
title = t.to_string();
|
||||
None => return Err(MpvError::MissingMpvData),
|
||||
};
|
||||
let title = match map.get("title") {
|
||||
Some(Value::String(s)) => s.to_string(),
|
||||
Some(data) => {
|
||||
return Err(MpvError::ValueContainsUnexpectedType {
|
||||
expected_type: "String".to_owned(),
|
||||
received: data.clone(),
|
||||
})
|
||||
}
|
||||
if let Value::Bool(ref b) = entry["current"] {
|
||||
current = *b;
|
||||
None => return Err(MpvError::MissingMpvData),
|
||||
};
|
||||
let current = match map.get("current") {
|
||||
Some(Value::Bool(b)) => *b,
|
||||
Some(data) => {
|
||||
return Err(MpvError::ValueContainsUnexpectedType {
|
||||
expected_type: "bool".to_owned(),
|
||||
received: data.clone(),
|
||||
})
|
||||
}
|
||||
output.push(PlaylistEntry {
|
||||
id,
|
||||
None => return Err(MpvError::MissingMpvData),
|
||||
};
|
||||
Ok(PlaylistEntry {
|
||||
id: 0,
|
||||
filename,
|
||||
title,
|
||||
current,
|
||||
});
|
||||
})
|
||||
}
|
||||
output
|
||||
|
||||
pub(crate) fn json_array_to_playlist(array: &[Value]) -> Result<Vec<PlaylistEntry>, MpvError> {
|
||||
array
|
||||
.iter()
|
||||
.map(|entry| match entry {
|
||||
Value::Object(map) => json_map_to_playlist_entry(map),
|
||||
data => Err(MpvError::ValueContainsUnexpectedType {
|
||||
expected_type: "Map<String, Value>".to_owned(),
|
||||
received: data.clone(),
|
||||
}),
|
||||
})
|
||||
.enumerate()
|
||||
.map(|(id, entry)| {
|
||||
entry.map(|mut entry| {
|
||||
entry.id = id;
|
||||
entry
|
||||
})
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -277,7 +313,7 @@ mod test {
|
|||
}
|
||||
|
||||
#[test]
|
||||
fn test_json_array_to_playlist() {
|
||||
fn test_json_array_to_playlist() -> Result<(), MpvError> {
|
||||
let json = json!([
|
||||
{
|
||||
"filename": "file1",
|
||||
|
@ -306,6 +342,8 @@ mod test {
|
|||
},
|
||||
];
|
||||
|
||||
assert_eq!(json_array_to_playlist(json.as_array().unwrap()), expected);
|
||||
assert_eq!(json_array_to_playlist(json.as_array().unwrap())?, expected);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,26 +1,30 @@
|
|||
use mpvipc::MpvExt;
|
||||
use mpvipc::{MpvError, MpvExt};
|
||||
|
||||
use super::*;
|
||||
|
||||
#[tokio::test]
|
||||
#[cfg(target_family = "unix")]
|
||||
async fn test_get_mpv_version() {
|
||||
async fn test_get_mpv_version() -> Result<(), MpvError> {
|
||||
let (mut proc, mpv) = spawn_headless_mpv().await.unwrap();
|
||||
let version: String = mpv.get_property("mpv-version").await.unwrap();
|
||||
let version: String = mpv.get_property("mpv-version").await?.unwrap();
|
||||
assert!(version.starts_with("mpv"));
|
||||
|
||||
mpv.kill().await.unwrap();
|
||||
proc.kill().await.unwrap();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
#[cfg(target_family = "unix")]
|
||||
async fn test_set_property() {
|
||||
async fn test_set_property() -> Result<(), MpvError> {
|
||||
let (mut proc, mpv) = spawn_headless_mpv().await.unwrap();
|
||||
mpv.set_property("pause", true).await.unwrap();
|
||||
let paused: bool = mpv.get_property("pause").await.unwrap();
|
||||
let paused: bool = mpv.get_property("pause").await?.unwrap();
|
||||
assert!(paused);
|
||||
|
||||
mpv.kill().await.unwrap();
|
||||
proc.kill().await.unwrap();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -22,73 +22,77 @@ fn test_socket(answers: Vec<String>) -> (UnixStream, JoinHandle<Result<(), Lines
|
|||
}
|
||||
|
||||
#[test(tokio::test)]
|
||||
async fn test_get_property_successful() {
|
||||
async fn test_get_property_successful() -> Result<(), MpvError> {
|
||||
let (server, join_handle) = test_socket(vec![
|
||||
json!({ "data": 100.0, "request_id": 0, "error": "success" }).to_string(),
|
||||
]);
|
||||
|
||||
let mpv = Mpv::connect_socket(server).await.unwrap();
|
||||
let volume: f64 = mpv.get_property("volume").await.unwrap();
|
||||
let mpv = Mpv::connect_socket(server).await?;
|
||||
let volume: Option<f64> = mpv.get_property("volume").await?;
|
||||
|
||||
assert_eq!(volume, 100.0);
|
||||
assert_eq!(volume, Some(100.0));
|
||||
join_handle.await.unwrap().unwrap();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test(tokio::test)]
|
||||
async fn test_get_property_broken_pipe() {
|
||||
async fn test_get_property_broken_pipe() -> Result<(), MpvError> {
|
||||
let (server, join_handle) = test_socket(vec![]);
|
||||
|
||||
let mpv = Mpv::connect_socket(server).await.unwrap();
|
||||
let maybe_volume = mpv.get_property::<f64>("volume").await;
|
||||
|
||||
match maybe_volume {
|
||||
Err(MpvError::MpvSocketConnectionError(err)) => {
|
||||
assert_eq!(err.to_string(), "Broken pipe (os error 32)");
|
||||
}
|
||||
_ => panic!("Unexpected result: {:?}", maybe_volume),
|
||||
}
|
||||
assert_eq!(
|
||||
maybe_volume,
|
||||
Err(MpvError::MpvSocketConnectionError(
|
||||
"Broken pipe (os error 32)".to_string()
|
||||
))
|
||||
);
|
||||
|
||||
join_handle.await.unwrap().unwrap();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test(tokio::test)]
|
||||
async fn test_get_property_wrong_type() {
|
||||
async fn test_get_property_wrong_type() -> Result<(), MpvError> {
|
||||
let (server, join_handle) = test_socket(vec![
|
||||
json!({ "data": 100.0, "request_id": 0, "error": "success" }).to_string(),
|
||||
]);
|
||||
|
||||
let mpv = Mpv::connect_socket(server).await.unwrap();
|
||||
let mpv = Mpv::connect_socket(server).await?;
|
||||
let maybe_volume = mpv.get_property::<bool>("volume").await;
|
||||
|
||||
match maybe_volume {
|
||||
assert_eq!(
|
||||
maybe_volume,
|
||||
Err(MpvError::ValueContainsUnexpectedType {
|
||||
expected_type,
|
||||
received,
|
||||
}) => {
|
||||
assert_eq!(expected_type, "bool");
|
||||
assert_eq!(received, json!(100.0));
|
||||
}
|
||||
_ => panic!("Unexpected result: {:?}", maybe_volume),
|
||||
}
|
||||
expected_type: "bool".to_string(),
|
||||
received: json!(100.0)
|
||||
})
|
||||
);
|
||||
join_handle.await.unwrap().unwrap();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test(tokio::test)]
|
||||
async fn test_get_property_error() {
|
||||
async fn test_get_property_error() -> Result<(), MpvError> {
|
||||
let (server, join_handle) = test_socket(vec![
|
||||
json!({ "error": "property unavailable", "request_id": 0 }).to_string(),
|
||||
]);
|
||||
|
||||
let mpv = Mpv::connect_socket(server).await.unwrap();
|
||||
let mpv = Mpv::connect_socket(server).await?;
|
||||
let maybe_volume = mpv.get_property::<f64>("volume").await;
|
||||
|
||||
match maybe_volume {
|
||||
Err(MpvError::MpvError(err)) => {
|
||||
assert_eq!(err, "property unavailable");
|
||||
}
|
||||
_ => panic!("Unexpected result: {:?}", maybe_volume),
|
||||
}
|
||||
assert_eq!(
|
||||
maybe_volume,
|
||||
Err(MpvError::MpvError("property unavailable".to_string()))
|
||||
);
|
||||
|
||||
join_handle.await.unwrap().unwrap();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test(tokio::test)]
|
||||
|
@ -130,8 +134,8 @@ async fn test_get_property_simultaneous_requests() {
|
|||
let mpv_clone_1 = mpv.clone();
|
||||
let mpv_poller_1 = tokio::spawn(async move {
|
||||
loop {
|
||||
let volume: f64 = mpv_clone_1.get_property("volume").await.unwrap();
|
||||
assert_eq!(volume, 100.0);
|
||||
let volume: Option<f64> = mpv_clone_1.get_property("volume").await.unwrap();
|
||||
assert_eq!(volume, Some(100.0));
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -139,8 +143,8 @@ async fn test_get_property_simultaneous_requests() {
|
|||
let mpv_poller_2 = tokio::spawn(async move {
|
||||
loop {
|
||||
tokio::time::sleep(Duration::from_millis(1)).await;
|
||||
let paused: bool = mpv_clone_2.get_property("pause").await.unwrap();
|
||||
assert!(paused);
|
||||
let paused: Option<bool> = mpv_clone_2.get_property("pause").await.unwrap();
|
||||
assert_eq!(paused, Some(true));
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -173,7 +177,7 @@ async fn test_get_property_simultaneous_requests() {
|
|||
}
|
||||
|
||||
#[test(tokio::test)]
|
||||
async fn test_get_playlist() {
|
||||
async fn test_get_playlist() -> Result<(), MpvError> {
|
||||
let expected = Playlist(vec![
|
||||
PlaylistEntry {
|
||||
id: 0,
|
||||
|
@ -208,22 +212,26 @@ async fn test_get_playlist() {
|
|||
})
|
||||
.to_string()]);
|
||||
|
||||
let mpv = Mpv::connect_socket(server).await.unwrap();
|
||||
let playlist = mpv.get_playlist().await.unwrap();
|
||||
let mpv = Mpv::connect_socket(server).await?;
|
||||
let playlist = mpv.get_playlist().await?;
|
||||
|
||||
assert_eq!(playlist, expected);
|
||||
join_handle.await.unwrap().unwrap();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test(tokio::test)]
|
||||
async fn test_get_playlist_empty() {
|
||||
async fn test_get_playlist_empty() -> Result<(), MpvError> {
|
||||
let (server, join_handle) = test_socket(vec![
|
||||
json!({ "data": [], "request_id": 0, "error": "success" }).to_string(),
|
||||
]);
|
||||
|
||||
let mpv = Mpv::connect_socket(server).await.unwrap();
|
||||
let playlist = mpv.get_playlist().await.unwrap();
|
||||
let mpv = Mpv::connect_socket(server).await?;
|
||||
let playlist = mpv.get_playlist().await?;
|
||||
|
||||
assert_eq!(playlist, Playlist(vec![]));
|
||||
join_handle.await.unwrap().unwrap();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use std::{panic, time::Duration};
|
||||
|
||||
use futures::{stream::FuturesUnordered, SinkExt, StreamExt};
|
||||
use mpvipc::{Mpv, MpvError, MpvExt, Playlist, PlaylistEntry};
|
||||
use mpvipc::{Mpv, MpvError};
|
||||
use serde_json::{json, Value};
|
||||
use test_log::test;
|
||||
use tokio::{net::UnixStream, task::JoinHandle};
|
||||
|
@ -22,69 +22,76 @@ fn test_socket(answers: Vec<String>) -> (UnixStream, JoinHandle<Result<(), Lines
|
|||
}
|
||||
|
||||
#[test(tokio::test)]
|
||||
async fn test_set_property_successful() {
|
||||
async fn test_set_property_successful() -> Result<(), MpvError> {
|
||||
let (server, join_handle) = test_socket(vec![
|
||||
json!({ "data": null, "request_id": 0, "error": "success" }).to_string(),
|
||||
]);
|
||||
|
||||
let mpv = Mpv::connect_socket(server).await.unwrap();
|
||||
let volume = mpv.set_property("volume", 64.0).await;
|
||||
let mpv = Mpv::connect_socket(server).await?;
|
||||
mpv.set_property("volume", 64.0).await?;
|
||||
|
||||
assert!(volume.is_ok());
|
||||
join_handle.await.unwrap().unwrap();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test(tokio::test)]
|
||||
async fn test_set_property_broken_pipe() {
|
||||
async fn test_set_property_broken_pipe() -> Result<(), MpvError> {
|
||||
let (server, join_handle) = test_socket(vec![]);
|
||||
|
||||
let mpv = Mpv::connect_socket(server).await.unwrap();
|
||||
let mpv = Mpv::connect_socket(server).await?;
|
||||
let maybe_set_volume = mpv.set_property("volume", 64.0).await;
|
||||
|
||||
match maybe_set_volume {
|
||||
Err(MpvError::MpvSocketConnectionError(err)) => {
|
||||
assert_eq!(err.to_string(), "Broken pipe (os error 32)");
|
||||
}
|
||||
_ => panic!("Unexpected result: {:?}", maybe_set_volume),
|
||||
}
|
||||
assert_eq!(
|
||||
maybe_set_volume,
|
||||
Err(MpvError::MpvSocketConnectionError(
|
||||
"Broken pipe (os error 32)".to_string()
|
||||
))
|
||||
);
|
||||
|
||||
join_handle.await.unwrap().unwrap();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test(tokio::test)]
|
||||
async fn test_set_property_wrong_type() {
|
||||
async fn test_set_property_wrong_type() -> Result<(), MpvError> {
|
||||
let (server, join_handle) = test_socket(vec![
|
||||
json!({"request_id":0,"error":"unsupported format for accessing property"}).to_string(),
|
||||
]);
|
||||
|
||||
let mpv = Mpv::connect_socket(server).await.unwrap();
|
||||
let mpv = Mpv::connect_socket(server).await?;
|
||||
let maybe_volume = mpv.set_property::<bool>("volume", true).await;
|
||||
|
||||
match maybe_volume {
|
||||
Err(MpvError::MpvError(err)) => {
|
||||
assert_eq!(err, "unsupported format for accessing property");
|
||||
}
|
||||
_ => panic!("Unexpected result: {:?}", maybe_volume),
|
||||
}
|
||||
assert_eq!(
|
||||
maybe_volume,
|
||||
Err(MpvError::MpvError(
|
||||
"unsupported format for accessing property".to_string()
|
||||
))
|
||||
);
|
||||
|
||||
join_handle.await.unwrap().unwrap();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test(tokio::test)]
|
||||
async fn test_get_property_error() {
|
||||
async fn test_get_property_error() -> Result<(), MpvError> {
|
||||
let (server, join_handle) = test_socket(vec![
|
||||
json!({"request_id":0,"error":"property not found"}).to_string(),
|
||||
]);
|
||||
|
||||
let mpv = Mpv::connect_socket(server).await.unwrap();
|
||||
let mpv = Mpv::connect_socket(server).await?;
|
||||
let maybe_volume = mpv.set_property("nonexistent", true).await;
|
||||
|
||||
match maybe_volume {
|
||||
Err(MpvError::MpvError(err)) => {
|
||||
assert_eq!(err, "property not found");
|
||||
}
|
||||
_ => panic!("Unexpected result: {:?}", maybe_volume),
|
||||
}
|
||||
assert_eq!(
|
||||
maybe_volume,
|
||||
Err(MpvError::MpvError("property not found".to_string()))
|
||||
);
|
||||
|
||||
join_handle.await.unwrap().unwrap();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test(tokio::test)]
|
||||
|
@ -175,59 +182,3 @@ async fn test_set_property_simultaneous_requests() {
|
|||
panic!("One of the pollers quit unexpectedly");
|
||||
};
|
||||
}
|
||||
|
||||
#[test(tokio::test)]
|
||||
async fn test_get_playlist() {
|
||||
let expected = Playlist(vec![
|
||||
PlaylistEntry {
|
||||
id: 0,
|
||||
filename: "file1".to_string(),
|
||||
title: "title1".to_string(),
|
||||
current: false,
|
||||
},
|
||||
PlaylistEntry {
|
||||
id: 1,
|
||||
filename: "file2".to_string(),
|
||||
title: "title2".to_string(),
|
||||
current: true,
|
||||
},
|
||||
PlaylistEntry {
|
||||
id: 2,
|
||||
filename: "file3".to_string(),
|
||||
title: "title3".to_string(),
|
||||
current: false,
|
||||
},
|
||||
]);
|
||||
|
||||
let (server, join_handle) = test_socket(vec![json!({
|
||||
"data": expected.0.iter().map(|entry| {
|
||||
json!({
|
||||
"filename": entry.filename,
|
||||
"title": entry.title,
|
||||
"current": entry.current
|
||||
})
|
||||
}).collect::<Vec<Value>>(),
|
||||
"request_id": 0,
|
||||
"error": "success"
|
||||
})
|
||||
.to_string()]);
|
||||
|
||||
let mpv = Mpv::connect_socket(server).await.unwrap();
|
||||
let playlist = mpv.get_playlist().await.unwrap();
|
||||
|
||||
assert_eq!(playlist, expected);
|
||||
join_handle.await.unwrap().unwrap();
|
||||
}
|
||||
|
||||
#[test(tokio::test)]
|
||||
async fn test_get_playlist_empty() {
|
||||
let (server, join_handle) = test_socket(vec![
|
||||
json!({ "data": [], "request_id": 0, "error": "success" }).to_string(),
|
||||
]);
|
||||
|
||||
let mpv = Mpv::connect_socket(server).await.unwrap();
|
||||
let playlist = mpv.get_playlist().await.unwrap();
|
||||
|
||||
assert_eq!(playlist, Playlist(vec![]));
|
||||
join_handle.await.unwrap().unwrap();
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue