From 93366593c78819123cb122b322cc245e718d955a Mon Sep 17 00:00:00 2001 From: h7x4 Date: Sun, 5 May 2024 13:20:07 +0200 Subject: [PATCH] Allow a few more highlevel types to be nullable --- src/core_api.rs | 2 +- src/highlevel_api_extension.rs | 8 +++--- src/ipc.rs | 1 + src/message_parser.rs | 20 +++++++++---- src/property_parser.rs | 37 ++++++++++++++++--------- tests/integration_tests/misc.rs | 27 ++++++++++++++++++ tests/mock_socket_tests/get_property.rs | 14 +++++----- 7 files changed, 79 insertions(+), 30 deletions(-) diff --git a/src/core_api.rs b/src/core_api.rs index 49636a2..d5e1517 100644 --- a/src/core_api.rs +++ b/src/core_api.rs @@ -90,7 +90,7 @@ pub struct Playlist(pub Vec); pub struct PlaylistEntry { pub id: usize, pub filename: String, - pub title: String, + pub title: Option, pub current: bool, } diff --git a/src/highlevel_api_extension.rs b/src/highlevel_api_extension.rs index 9f50897..de760ab 100644 --- a/src/highlevel_api_extension.rs +++ b/src/highlevel_api_extension.rs @@ -154,10 +154,10 @@ pub trait MpvExt { async fn get_speed(&self) -> Result; /// Get the current position in the current video. - async fn get_time_pos(&self) -> Result; + async fn get_time_pos(&self) -> Result, MpvError>; /// Get the amount of time remaining in the current video. - async fn get_time_remaining(&self) -> Result; + async fn get_time_remaining(&self) -> Result, MpvError>; /// Get the total duration of the current video. async fn get_duration(&self) -> Result; @@ -415,7 +415,7 @@ impl MpvExt for Mpv { } } - async fn get_time_pos(&self) -> Result { + async fn get_time_pos(&self) -> Result, MpvError> { let data = self.get_property("time-pos").await?; match parse_property("time-pos", data)? { Property::TimePos(value) => Ok(value), @@ -423,7 +423,7 @@ impl MpvExt for Mpv { } } - async fn get_time_remaining(&self) -> Result { + async fn get_time_remaining(&self) -> Result, MpvError> { let data = self.get_property("time-remaining").await?; match parse_property("time-remaining", data)? { Property::TimeRemaining(value) => Ok(value), diff --git a/src/ipc.rs b/src/ipc.rs index 0310c82..bdb593b 100644 --- a/src/ipc.rs +++ b/src/ipc.rs @@ -224,6 +224,7 @@ fn parse_mpv_response_data(value: Value) -> Result, MpvError> { }) .and_then(|(error, data)| match error { "success" => Ok(data), + "property unavailable" => Ok(None), err => Err(MpvError::MpvError(err.to_string())), }); diff --git a/src/message_parser.rs b/src/message_parser.rs index 3955082..25febe5 100644 --- a/src/message_parser.rs +++ b/src/message_parser.rs @@ -169,14 +169,14 @@ fn json_map_to_playlist_entry( None => return Err(MpvError::MissingMpvData), }; let title = match map.get("title") { - Some(Value::String(s)) => s.to_string(), + Some(Value::String(s)) => Some(s.to_string()), Some(data) => { return Err(MpvError::ValueContainsUnexpectedType { expected_type: "String".to_owned(), received: data.clone(), }) } - None => return Err(MpvError::MissingMpvData), + None => None, }; let current = match map.get("current") { Some(Value::Bool(b)) => *b, @@ -186,7 +186,7 @@ fn json_map_to_playlist_entry( received: data.clone(), }) } - None => return Err(MpvError::MissingMpvData), + None => false, }; Ok(PlaylistEntry { id: 0, @@ -324,6 +324,10 @@ mod test { "filename": "file2", "title": "title2", "current": false + }, + { + "filename": "file3", + "current": false } ]); @@ -331,13 +335,19 @@ mod test { PlaylistEntry { id: 0, filename: "file1".to_string(), - title: "title1".to_string(), + title: Some("title1".to_string()), current: true, }, PlaylistEntry { id: 1, 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, }, ]; diff --git a/src/property_parser.rs b/src/property_parser.rs index 6995b61..5b1586f 100644 --- a/src/property_parser.rs +++ b/src/property_parser.rs @@ -34,11 +34,12 @@ pub enum Property { PlaylistPos(Option), LoopFile(LoopProperty), LoopPlaylist(LoopProperty), - TimePos(f64), - TimeRemaining(f64), + TimePos(Option), + TimeRemaining(Option), Speed(f64), Volume(f64), Mute(bool), + EofReached(bool), Unknown { name: String, data: Option, @@ -204,31 +205,28 @@ pub fn parse_property(name: &str, data: Option) -> Result { let time_pos = match data { - Some(MpvDataType::Double(d)) => d, + Some(MpvDataType::Double(d)) => Some(d), Some(data) => { return Err(MpvError::DataContainsUnexpectedType { expected_type: "f64".to_owned(), received: data, }) } - None => { - return Err(MpvError::MissingMpvData); - } + None => None, }; + Ok(Property::TimePos(time_pos)) } "time-remaining" => { let time_remaining = match data { - Some(MpvDataType::Double(d)) => d, + Some(MpvDataType::Double(d)) => Some(d), Some(data) => { return Err(MpvError::DataContainsUnexpectedType { expected_type: "f64".to_owned(), received: data, }) } - None => { - return Err(MpvError::MissingMpvData); - } + None => None, }; Ok(Property::TimeRemaining(time_remaining)) } @@ -277,6 +275,19 @@ pub fn parse_property(name: &str, data: Option) -> Result { + 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 _ => Ok(Property::Unknown { name: name.to_owned(), @@ -299,14 +310,14 @@ fn mpv_data_to_playlist_entry( None => return Err(MpvError::MissingMpvData), }; let title = match map.get("title") { - Some(MpvDataType::String(s)) => s.to_string(), + Some(MpvDataType::String(s)) => Some(s.to_string()), Some(data) => { return Err(MpvError::DataContainsUnexpectedType { expected_type: "String".to_owned(), received: data.clone(), }) } - None => return Err(MpvError::MissingMpvData), + None => None, }; let current = match map.get("current") { Some(MpvDataType::Bool(b)) => *b, @@ -316,7 +327,7 @@ fn mpv_data_to_playlist_entry( received: data.clone(), }) } - None => return Err(MpvError::MissingMpvData), + None => false }; Ok(PlaylistEntry { id: 0, diff --git a/tests/integration_tests/misc.rs b/tests/integration_tests/misc.rs index 4f3a56f..709f755 100644 --- a/tests/integration_tests/misc.rs +++ b/tests/integration_tests/misc.rs @@ -28,3 +28,30 @@ async fn test_set_property() -> Result<(), MpvError> { 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::("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::("nonexistent").await; + assert_eq!(nonexistent, Err(MpvError::MpvError("property not found".to_string()))); + + mpv.kill().await.unwrap(); + proc.kill().await.unwrap(); + + Ok(()) +} \ No newline at end of file diff --git a/tests/mock_socket_tests/get_property.rs b/tests/mock_socket_tests/get_property.rs index 1ce44e4..ab58695 100644 --- a/tests/mock_socket_tests/get_property.rs +++ b/tests/mock_socket_tests/get_property.rs @@ -77,7 +77,7 @@ async fn test_get_property_wrong_type() -> Result<(), MpvError> { } #[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![ json!({ "error": "property unavailable", "request_id": 0 }).to_string(), ]); @@ -87,7 +87,7 @@ async fn test_get_property_error() -> Result<(), MpvError> { assert_eq!( maybe_volume, - Err(MpvError::MpvError("property unavailable".to_string())) + Ok(None), ); join_handle.await.unwrap().unwrap(); @@ -119,7 +119,7 @@ async fn test_get_property_simultaneous_requests() { } _ => { 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(); } } @@ -155,7 +155,7 @@ async fn test_get_property_simultaneous_requests() { let maybe_volume = mpv_clone_3.get_property::("nonexistent").await; match maybe_volume { Err(MpvError::MpvError(err)) => { - assert_eq!(err, "property unavailable"); + assert_eq!(err, "property not found"); } _ => panic!("Unexpected result: {:?}", maybe_volume), } @@ -182,19 +182,19 @@ async fn test_get_playlist() -> Result<(), MpvError> { PlaylistEntry { id: 0, filename: "file1".to_string(), - title: "title1".to_string(), + title: Some("title1".to_string()), current: false, }, PlaylistEntry { id: 1, filename: "file2".to_string(), - title: "title2".to_string(), + title: Some("title2".to_string()), current: true, }, PlaylistEntry { id: 2, filename: "file3".to_string(), - title: "title3".to_string(), + title: Some("title3".to_string()), current: false, }, ]);