//! Module containing a high-level client for interacting with an MPD server. //! It provides methods for common operations such as playing, pausing, and //! managing the playlist, and returns the expected response types directly //! from its methods. use crate::{Request, commands::*, types::SongPosition}; #[cfg(feature = "futures")] use futures_util::{ AsyncBufReadExt, io::{AsyncRead, AsyncWrite, AsyncWriteExt, BufReader}, }; use thiserror::Error; #[cfg(feature = "tokio")] use tokio::io::{AsyncBufReadExt, AsyncRead, AsyncWrite, AsyncWriteExt, BufReader}; pub struct MpdClient<'a, T> where T: AsyncWrite + AsyncRead + Unpin, { connection: &'a mut T, } #[derive(Error, Debug)] pub enum MpdClientError { #[error("Connection error: {0}")] ConnectionError(#[from] std::io::Error), #[error("Failed to parse MPD response: {0}")] ResponseParseError(#[from] crate::commands::ResponseParserError), #[error("MPD returned an error: {0}")] MpdError(#[from] crate::response::MpdError), } impl<'a, T> MpdClient<'a, T> where T: AsyncWrite + AsyncRead + Unpin, { pub fn new(connection: &'a mut T) -> Self { MpdClient { connection } } pub async fn read_initial_mpd_version(&mut self) -> Result { let mut reader = BufReader::new(&mut self.connection); let mut version_line = String::new(); reader .read_line(&mut version_line) .await .map_err(MpdClientError::ConnectionError)?; Ok(version_line.trim().to_string()) } async fn read_response(&mut self) -> Result, MpdClientError> { let mut response = Vec::new(); let mut reader = BufReader::new(&mut self.connection); loop { let mut line = Vec::new(); let bytes_read = reader .read_until(b'\n', &mut line) .await .map_err(MpdClientError::ConnectionError)?; if bytes_read == 0 { break; // EOF reached } response.extend_from_slice(&line); if line == b"OK\n" || line.starts_with(b"ACK ") { break; // End of response } } Ok(response) } pub async fn play( &mut self, position: Option, ) -> Result { let message = Request::Play(position); let payload = message.serialize(); self.connection .write_all(payload.as_bytes()) .await .map_err(MpdClientError::ConnectionError)?; self.connection .flush() .await .map_err(MpdClientError::ConnectionError)?; let response_bytes = self.read_response().await?; let response = PlayResponse::parse_raw(&response_bytes)?; Ok(response) } }