106 lines
2.8 KiB
Rust
106 lines
2.8 KiB
Rust
//! A high-level client for interacting with an Mpd server.
|
|
//!
|
|
//! The client 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<String, MpdClientError> {
|
|
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<Vec<u8>, 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<SongPosition>,
|
|
) -> Result<PlayResponse, MpdClientError> {
|
|
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)
|
|
}
|
|
}
|