From f2e2eb271a6d8431e0b440c0c8198666364d372b Mon Sep 17 00:00:00 2001 From: h7x4 Date: Mon, 8 Dec 2025 13:05:30 +0900 Subject: [PATCH] commands: add command executor directly on `Command` trait --- src/commands.rs | 59 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) diff --git a/src/commands.rs b/src/commands.rs index e4eab65..38b2d0f 100644 --- a/src/commands.rs +++ b/src/commands.rs @@ -37,6 +37,16 @@ pub use reflection::*; pub use stickers::*; pub use stored_playlists::*; +#[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}; + /// A trait modelling a single MPD command request. pub trait CommandRequest where @@ -159,6 +169,55 @@ pub trait Command { fn parse_raw_response(raw: &[u8]) -> Result { Self::Response::parse_raw(raw) } + + async fn execute( + request: Self::Request, + connection: &mut T, + ) -> Result + where + Self: Sized, + T: AsyncWrite + AsyncRead + Unpin, + { + let payload = request.serialize(); + + connection + .write_all(payload.as_bytes()) + .await + .map_err(crate::MpdClientError::ConnectionError)?; + + connection + .flush() + .await + .map_err(crate::MpdClientError::ConnectionError)?; + + let mut response_bytes = Vec::new(); + let mut reader = BufReader::new(connection); + + loop { + let mut line = Vec::new(); + + let bytes_read = reader + .read_until(b'\n', &mut line) + .await + .map_err(crate::MpdClientError::ConnectionError)?; + + if bytes_read == 0 { + break; // EOF reached + } + + response_bytes.extend_from_slice(&line); + + // TODO: handle errors properly + if line == b"OK\n" || line.starts_with(b"ACK ") { + break; // End of response + } + } + + let response = Self::parse_raw_response(&response_bytes) + .map_err(crate::MpdClientError::ResponseParseError)?; + + Ok(response) + } } // Request/response implementation helpers