client: init
This commit is contained in:
@@ -12,10 +12,17 @@ rust-version = "1.85.0"
|
||||
|
||||
[dependencies]
|
||||
chrono = { version = "0.4.42", features = ["serde"] }
|
||||
futures-util = { version = "0.3.31", optional = true, features = ["io"] }
|
||||
lalrpop-util = { version = "0.22.2", features = ["lexer"] }
|
||||
paste = "1.0.15"
|
||||
serde = { version = "1.0.228", features = ["derive"] }
|
||||
thiserror = "2.0.17"
|
||||
tokio = { version = "1.48.0", optional = true, features = ["io-util"] }
|
||||
|
||||
[features]
|
||||
default = ["tokio"]
|
||||
futures = ["dep:futures-util"]
|
||||
tokio = ["dep:tokio"]
|
||||
|
||||
[dev-dependencies]
|
||||
indoc = "2.0.7"
|
||||
|
||||
106
src/client.rs
Normal file
106
src/client.rs
Normal file
@@ -0,0 +1,106 @@
|
||||
//! 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 }
|
||||
}
|
||||
|
||||
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)?;
|
||||
|
||||
return 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)
|
||||
}
|
||||
}
|
||||
@@ -5,16 +5,16 @@
|
||||
|
||||
mod client;
|
||||
mod commands;
|
||||
mod filter;
|
||||
mod request;
|
||||
mod request_tokenizer;
|
||||
mod response;
|
||||
mod response_tokenizer;
|
||||
mod server;
|
||||
|
||||
pub mod filter;
|
||||
pub mod types;
|
||||
|
||||
pub use filter::{CaseSensitivity, ComparisonOperator, Filter};
|
||||
pub use client::MpdClient;
|
||||
pub use request::Request;
|
||||
pub use response::Response;
|
||||
pub use server::MPDServer;
|
||||
pub use server::MpdServer;
|
||||
|
||||
@@ -224,7 +224,7 @@ macro_rules! parse_req {
|
||||
}
|
||||
|
||||
impl Request {
|
||||
fn serialize(self) -> String {
|
||||
pub fn serialize(self) -> String {
|
||||
match self {
|
||||
/* querying mpd status */
|
||||
Request::ClearError => serialize_req!(self, ClearErrorRequest),
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use crate::{Request, Response, types::SubSystem};
|
||||
|
||||
pub trait MPDServer {
|
||||
pub trait MpdServer {
|
||||
type Error;
|
||||
|
||||
fn route_request(&mut self, request: Request) -> Result<Response, Self::Error> {
|
||||
|
||||
Reference in New Issue
Block a user