1
//! A high-level client for interacting with an Mpd server.
2
//!
3
//! The client provides methods for common operations such as playing, pausing, and
4
//! managing the playlist, and returns the expected response types directly
5
//! from its methods.
6

            
7
use crate::{Request, commands::*, types::SongPosition};
8

            
9
#[cfg(feature = "futures")]
10
use futures_util::{
11
    AsyncBufReadExt,
12
    io::{AsyncRead, AsyncWrite, AsyncWriteExt, BufReader},
13
};
14

            
15
use thiserror::Error;
16
#[cfg(feature = "tokio")]
17
use tokio::io::{AsyncBufReadExt, AsyncRead, AsyncWrite, AsyncWriteExt, BufReader};
18

            
19
pub struct MpdClient<'a, T>
20
where
21
    T: AsyncWrite + AsyncRead + Unpin,
22
{
23
    connection: &'a mut T,
24
}
25

            
26
#[derive(Error, Debug)]
27
pub enum MpdClientError {
28
    #[error("Connection error: {0}")]
29
    ConnectionError(#[from] std::io::Error),
30

            
31
    #[error("Failed to parse MPD response: {0}")]
32
    ResponseParseError(#[from] crate::commands::ResponseParserError),
33

            
34
    #[error("MPD returned an error: {0}")]
35
    MpdError(#[from] crate::response::MpdError),
36
}
37

            
38
impl<'a, T> MpdClient<'a, T>
39
where
40
    T: AsyncWrite + AsyncRead + Unpin,
41
{
42
    pub fn new(connection: &'a mut T) -> Self {
43
        MpdClient { connection }
44
    }
45

            
46
    pub async fn read_initial_mpd_version(&mut self) -> Result<String, MpdClientError> {
47
        let mut reader = BufReader::new(&mut self.connection);
48
        let mut version_line = String::new();
49

            
50
        reader
51
            .read_line(&mut version_line)
52
            .await
53
            .map_err(MpdClientError::ConnectionError)?;
54

            
55
        Ok(version_line.trim().to_string())
56
    }
57

            
58
    async fn read_response(&mut self) -> Result<Vec<u8>, MpdClientError> {
59
        let mut response = Vec::new();
60
        let mut reader = BufReader::new(&mut self.connection);
61

            
62
        loop {
63
            let mut line = Vec::new();
64

            
65
            let bytes_read = reader
66
                .read_until(b'\n', &mut line)
67
                .await
68
                .map_err(MpdClientError::ConnectionError)?;
69

            
70
            if bytes_read == 0 {
71
                break; // EOF reached
72
            }
73

            
74
            response.extend_from_slice(&line);
75

            
76
            if line == b"OK\n" || line.starts_with(b"ACK ") {
77
                break; // End of response
78
            }
79
        }
80

            
81
        Ok(response)
82
    }
83

            
84
    pub async fn play(
85
        &mut self,
86
        position: Option<SongPosition>,
87
    ) -> Result<PlayResponse, MpdClientError> {
88
        let message = Request::Play(position);
89
        let payload = message.serialize();
90

            
91
        self.connection
92
            .write_all(payload.as_bytes())
93
            .await
94
            .map_err(MpdClientError::ConnectionError)?;
95

            
96
        self.connection
97
            .flush()
98
            .await
99
            .map_err(MpdClientError::ConnectionError)?;
100

            
101
        let response_bytes = self.read_response().await?;
102
        let response = PlayResponse::parse_raw(&response_bytes)?;
103
        Ok(response)
104
    }
105
}