Allow a few more highlevel types to be nullable
This commit is contained in:
parent
4cc824d164
commit
93366593c7
|
@ -90,7 +90,7 @@ pub struct Playlist(pub Vec<PlaylistEntry>);
|
||||||
pub struct PlaylistEntry {
|
pub struct PlaylistEntry {
|
||||||
pub id: usize,
|
pub id: usize,
|
||||||
pub filename: String,
|
pub filename: String,
|
||||||
pub title: String,
|
pub title: Option<String>,
|
||||||
pub current: bool,
|
pub current: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -154,10 +154,10 @@ pub trait MpvExt {
|
||||||
async fn get_speed(&self) -> Result<f64, MpvError>;
|
async fn get_speed(&self) -> Result<f64, MpvError>;
|
||||||
|
|
||||||
/// Get the current position in the current video.
|
/// Get the current position in the current video.
|
||||||
async fn get_time_pos(&self) -> Result<f64, MpvError>;
|
async fn get_time_pos(&self) -> Result<Option<f64>, MpvError>;
|
||||||
|
|
||||||
/// Get the amount of time remaining in the current video.
|
/// Get the amount of time remaining in the current video.
|
||||||
async fn get_time_remaining(&self) -> Result<f64, MpvError>;
|
async fn get_time_remaining(&self) -> Result<Option<f64>, MpvError>;
|
||||||
|
|
||||||
/// Get the total duration of the current video.
|
/// Get the total duration of the current video.
|
||||||
async fn get_duration(&self) -> Result<f64, MpvError>;
|
async fn get_duration(&self) -> Result<f64, MpvError>;
|
||||||
|
@ -415,7 +415,7 @@ impl MpvExt for Mpv {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn get_time_pos(&self) -> Result<f64, MpvError> {
|
async fn get_time_pos(&self) -> Result<Option<f64>, MpvError> {
|
||||||
let data = self.get_property("time-pos").await?;
|
let data = self.get_property("time-pos").await?;
|
||||||
match parse_property("time-pos", data)? {
|
match parse_property("time-pos", data)? {
|
||||||
Property::TimePos(value) => Ok(value),
|
Property::TimePos(value) => Ok(value),
|
||||||
|
@ -423,7 +423,7 @@ impl MpvExt for Mpv {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn get_time_remaining(&self) -> Result<f64, MpvError> {
|
async fn get_time_remaining(&self) -> Result<Option<f64>, MpvError> {
|
||||||
let data = self.get_property("time-remaining").await?;
|
let data = self.get_property("time-remaining").await?;
|
||||||
match parse_property("time-remaining", data)? {
|
match parse_property("time-remaining", data)? {
|
||||||
Property::TimeRemaining(value) => Ok(value),
|
Property::TimeRemaining(value) => Ok(value),
|
||||||
|
|
|
@ -224,6 +224,7 @@ fn parse_mpv_response_data(value: Value) -> Result<Option<Value>, MpvError> {
|
||||||
})
|
})
|
||||||
.and_then(|(error, data)| match error {
|
.and_then(|(error, data)| match error {
|
||||||
"success" => Ok(data),
|
"success" => Ok(data),
|
||||||
|
"property unavailable" => Ok(None),
|
||||||
err => Err(MpvError::MpvError(err.to_string())),
|
err => Err(MpvError::MpvError(err.to_string())),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -169,14 +169,14 @@ fn json_map_to_playlist_entry(
|
||||||
None => return Err(MpvError::MissingMpvData),
|
None => return Err(MpvError::MissingMpvData),
|
||||||
};
|
};
|
||||||
let title = match map.get("title") {
|
let title = match map.get("title") {
|
||||||
Some(Value::String(s)) => s.to_string(),
|
Some(Value::String(s)) => Some(s.to_string()),
|
||||||
Some(data) => {
|
Some(data) => {
|
||||||
return Err(MpvError::ValueContainsUnexpectedType {
|
return Err(MpvError::ValueContainsUnexpectedType {
|
||||||
expected_type: "String".to_owned(),
|
expected_type: "String".to_owned(),
|
||||||
received: data.clone(),
|
received: data.clone(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
None => return Err(MpvError::MissingMpvData),
|
None => None,
|
||||||
};
|
};
|
||||||
let current = match map.get("current") {
|
let current = match map.get("current") {
|
||||||
Some(Value::Bool(b)) => *b,
|
Some(Value::Bool(b)) => *b,
|
||||||
|
@ -186,7 +186,7 @@ fn json_map_to_playlist_entry(
|
||||||
received: data.clone(),
|
received: data.clone(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
None => return Err(MpvError::MissingMpvData),
|
None => false,
|
||||||
};
|
};
|
||||||
Ok(PlaylistEntry {
|
Ok(PlaylistEntry {
|
||||||
id: 0,
|
id: 0,
|
||||||
|
@ -324,6 +324,10 @@ mod test {
|
||||||
"filename": "file2",
|
"filename": "file2",
|
||||||
"title": "title2",
|
"title": "title2",
|
||||||
"current": false
|
"current": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "file3",
|
||||||
|
"current": false
|
||||||
}
|
}
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
@ -331,13 +335,19 @@ mod test {
|
||||||
PlaylistEntry {
|
PlaylistEntry {
|
||||||
id: 0,
|
id: 0,
|
||||||
filename: "file1".to_string(),
|
filename: "file1".to_string(),
|
||||||
title: "title1".to_string(),
|
title: Some("title1".to_string()),
|
||||||
current: true,
|
current: true,
|
||||||
},
|
},
|
||||||
PlaylistEntry {
|
PlaylistEntry {
|
||||||
id: 1,
|
id: 1,
|
||||||
filename: "file2".to_string(),
|
filename: "file2".to_string(),
|
||||||
title: "title2".to_string(),
|
title: Some("title2".to_string()),
|
||||||
|
current: false,
|
||||||
|
},
|
||||||
|
PlaylistEntry {
|
||||||
|
id: 2,
|
||||||
|
filename: "file3".to_string(),
|
||||||
|
title: None,
|
||||||
current: false,
|
current: false,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
|
@ -34,11 +34,12 @@ pub enum Property {
|
||||||
PlaylistPos(Option<usize>),
|
PlaylistPos(Option<usize>),
|
||||||
LoopFile(LoopProperty),
|
LoopFile(LoopProperty),
|
||||||
LoopPlaylist(LoopProperty),
|
LoopPlaylist(LoopProperty),
|
||||||
TimePos(f64),
|
TimePos(Option<f64>),
|
||||||
TimeRemaining(f64),
|
TimeRemaining(Option<f64>),
|
||||||
Speed(f64),
|
Speed(f64),
|
||||||
Volume(f64),
|
Volume(f64),
|
||||||
Mute(bool),
|
Mute(bool),
|
||||||
|
EofReached(bool),
|
||||||
Unknown {
|
Unknown {
|
||||||
name: String,
|
name: String,
|
||||||
data: Option<MpvDataType>,
|
data: Option<MpvDataType>,
|
||||||
|
@ -204,31 +205,28 @@ pub fn parse_property(name: &str, data: Option<MpvDataType>) -> Result<Property,
|
||||||
}
|
}
|
||||||
"time-pos" => {
|
"time-pos" => {
|
||||||
let time_pos = match data {
|
let time_pos = match data {
|
||||||
Some(MpvDataType::Double(d)) => d,
|
Some(MpvDataType::Double(d)) => Some(d),
|
||||||
Some(data) => {
|
Some(data) => {
|
||||||
return Err(MpvError::DataContainsUnexpectedType {
|
return Err(MpvError::DataContainsUnexpectedType {
|
||||||
expected_type: "f64".to_owned(),
|
expected_type: "f64".to_owned(),
|
||||||
received: data,
|
received: data,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
None => {
|
None => None,
|
||||||
return Err(MpvError::MissingMpvData);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(Property::TimePos(time_pos))
|
Ok(Property::TimePos(time_pos))
|
||||||
}
|
}
|
||||||
"time-remaining" => {
|
"time-remaining" => {
|
||||||
let time_remaining = match data {
|
let time_remaining = match data {
|
||||||
Some(MpvDataType::Double(d)) => d,
|
Some(MpvDataType::Double(d)) => Some(d),
|
||||||
Some(data) => {
|
Some(data) => {
|
||||||
return Err(MpvError::DataContainsUnexpectedType {
|
return Err(MpvError::DataContainsUnexpectedType {
|
||||||
expected_type: "f64".to_owned(),
|
expected_type: "f64".to_owned(),
|
||||||
received: data,
|
received: data,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
None => {
|
None => None,
|
||||||
return Err(MpvError::MissingMpvData);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
Ok(Property::TimeRemaining(time_remaining))
|
Ok(Property::TimeRemaining(time_remaining))
|
||||||
}
|
}
|
||||||
|
@ -277,6 +275,19 @@ pub fn parse_property(name: &str, data: Option<MpvDataType>) -> Result<Property,
|
||||||
};
|
};
|
||||||
Ok(Property::Mute(mute))
|
Ok(Property::Mute(mute))
|
||||||
}
|
}
|
||||||
|
"eof-reached" => {
|
||||||
|
let eof_reached = match data {
|
||||||
|
Some(MpvDataType::Bool(b)) => b,
|
||||||
|
Some(data) => {
|
||||||
|
return Err(MpvError::DataContainsUnexpectedType {
|
||||||
|
expected_type: "bool".to_owned(),
|
||||||
|
received: data,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
None => true,
|
||||||
|
};
|
||||||
|
Ok(Property::EofReached(eof_reached))
|
||||||
|
}
|
||||||
// TODO: add missing cases
|
// TODO: add missing cases
|
||||||
_ => Ok(Property::Unknown {
|
_ => Ok(Property::Unknown {
|
||||||
name: name.to_owned(),
|
name: name.to_owned(),
|
||||||
|
@ -299,14 +310,14 @@ fn mpv_data_to_playlist_entry(
|
||||||
None => return Err(MpvError::MissingMpvData),
|
None => return Err(MpvError::MissingMpvData),
|
||||||
};
|
};
|
||||||
let title = match map.get("title") {
|
let title = match map.get("title") {
|
||||||
Some(MpvDataType::String(s)) => s.to_string(),
|
Some(MpvDataType::String(s)) => Some(s.to_string()),
|
||||||
Some(data) => {
|
Some(data) => {
|
||||||
return Err(MpvError::DataContainsUnexpectedType {
|
return Err(MpvError::DataContainsUnexpectedType {
|
||||||
expected_type: "String".to_owned(),
|
expected_type: "String".to_owned(),
|
||||||
received: data.clone(),
|
received: data.clone(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
None => return Err(MpvError::MissingMpvData),
|
None => None,
|
||||||
};
|
};
|
||||||
let current = match map.get("current") {
|
let current = match map.get("current") {
|
||||||
Some(MpvDataType::Bool(b)) => *b,
|
Some(MpvDataType::Bool(b)) => *b,
|
||||||
|
@ -316,7 +327,7 @@ fn mpv_data_to_playlist_entry(
|
||||||
received: data.clone(),
|
received: data.clone(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
None => return Err(MpvError::MissingMpvData),
|
None => false
|
||||||
};
|
};
|
||||||
Ok(PlaylistEntry {
|
Ok(PlaylistEntry {
|
||||||
id: 0,
|
id: 0,
|
||||||
|
|
|
@ -28,3 +28,30 @@ async fn test_set_property() -> Result<(), MpvError> {
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
#[cfg(target_family = "unix")]
|
||||||
|
async fn test_get_unavailable_property() -> Result<(), MpvError> {
|
||||||
|
let (mut proc, mpv) = spawn_headless_mpv().await.unwrap();
|
||||||
|
let time_pos = mpv.get_property::<f64>("time-pos").await;
|
||||||
|
assert_eq!(time_pos, Ok(None));
|
||||||
|
|
||||||
|
mpv.kill().await.unwrap();
|
||||||
|
proc.kill().await.unwrap();
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
#[cfg(target_family = "unix")]
|
||||||
|
async fn test_get_nonexistent_property() -> Result<(), MpvError> {
|
||||||
|
let (mut proc, mpv) = spawn_headless_mpv().await.unwrap();
|
||||||
|
let nonexistent = mpv.get_property::<f64>("nonexistent").await;
|
||||||
|
assert_eq!(nonexistent, Err(MpvError::MpvError("property not found".to_string())));
|
||||||
|
|
||||||
|
mpv.kill().await.unwrap();
|
||||||
|
proc.kill().await.unwrap();
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
|
@ -77,7 +77,7 @@ async fn test_get_property_wrong_type() -> Result<(), MpvError> {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test(tokio::test)]
|
#[test(tokio::test)]
|
||||||
async fn test_get_property_error() -> Result<(), MpvError> {
|
async fn test_get_unavailable_property() -> Result<(), MpvError> {
|
||||||
let (server, join_handle) = test_socket(vec![
|
let (server, join_handle) = test_socket(vec![
|
||||||
json!({ "error": "property unavailable", "request_id": 0 }).to_string(),
|
json!({ "error": "property unavailable", "request_id": 0 }).to_string(),
|
||||||
]);
|
]);
|
||||||
|
@ -87,7 +87,7 @@ async fn test_get_property_error() -> Result<(), MpvError> {
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
maybe_volume,
|
maybe_volume,
|
||||||
Err(MpvError::MpvError("property unavailable".to_string()))
|
Ok(None),
|
||||||
);
|
);
|
||||||
|
|
||||||
join_handle.await.unwrap().unwrap();
|
join_handle.await.unwrap().unwrap();
|
||||||
|
@ -119,7 +119,7 @@ async fn test_get_property_simultaneous_requests() {
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
let response =
|
let response =
|
||||||
json!({ "error": "property unavailable", "request_id": 0 }).to_string();
|
json!({ "error": "property not found", "request_id": 0 }).to_string();
|
||||||
framed.send(response).await.unwrap();
|
framed.send(response).await.unwrap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -155,7 +155,7 @@ async fn test_get_property_simultaneous_requests() {
|
||||||
let maybe_volume = mpv_clone_3.get_property::<f64>("nonexistent").await;
|
let maybe_volume = mpv_clone_3.get_property::<f64>("nonexistent").await;
|
||||||
match maybe_volume {
|
match maybe_volume {
|
||||||
Err(MpvError::MpvError(err)) => {
|
Err(MpvError::MpvError(err)) => {
|
||||||
assert_eq!(err, "property unavailable");
|
assert_eq!(err, "property not found");
|
||||||
}
|
}
|
||||||
_ => panic!("Unexpected result: {:?}", maybe_volume),
|
_ => panic!("Unexpected result: {:?}", maybe_volume),
|
||||||
}
|
}
|
||||||
|
@ -182,19 +182,19 @@ async fn test_get_playlist() -> Result<(), MpvError> {
|
||||||
PlaylistEntry {
|
PlaylistEntry {
|
||||||
id: 0,
|
id: 0,
|
||||||
filename: "file1".to_string(),
|
filename: "file1".to_string(),
|
||||||
title: "title1".to_string(),
|
title: Some("title1".to_string()),
|
||||||
current: false,
|
current: false,
|
||||||
},
|
},
|
||||||
PlaylistEntry {
|
PlaylistEntry {
|
||||||
id: 1,
|
id: 1,
|
||||||
filename: "file2".to_string(),
|
filename: "file2".to_string(),
|
||||||
title: "title2".to_string(),
|
title: Some("title2".to_string()),
|
||||||
current: true,
|
current: true,
|
||||||
},
|
},
|
||||||
PlaylistEntry {
|
PlaylistEntry {
|
||||||
id: 2,
|
id: 2,
|
||||||
filename: "file3".to_string(),
|
filename: "file3".to_string(),
|
||||||
title: "title3".to_string(),
|
title: Some("title3".to_string()),
|
||||||
current: false,
|
current: false,
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
|
|
Loading…
Reference in New Issue