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, BufWriter},
13
};
14

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

            
18
use thiserror::Error;
19

            
20
pub struct MpdClient<'a, T>
21
where
22
    T: AsyncWrite + AsyncRead + Unpin,
23
{
24
    stream: BufStream<&'a mut T>,
25
    mpd_version: Option<String>,
26
}
27

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

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

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

            
40
impl<'a, T> MpdClient<'a, T>
41
where
42
    T: AsyncWrite + AsyncRead + Unpin,
43
{
44
    pub async fn new(connection: &'a mut T) -> Result<Self, MpdClientError> {
45
        let mut client = MpdClient {
46
            stream: BufStream::new(connection),
47
            mpd_version: None,
48
        };
49

            
50
        client.read_initial_mpd_version().await?;
51

            
52
        Ok(client)
53
    }
54

            
55
    pub async fn wrap_existing(connection: &'a mut T, mpd_version: Option<String>) -> Self {
56
        MpdClient {
57
            stream: BufStream::new(connection),
58
            mpd_version,
59
        }
60
    }
61

            
62
    pub fn into_connection(self) -> &'a mut T {
63
        self.stream.into_inner()
64
    }
65

            
66
    pub fn get_mpd_version(&self) -> Option<&str> {
67
        self.mpd_version.as_deref()
68
    }
69

            
70
    async fn read_initial_mpd_version(&mut self) -> Result<(), MpdClientError> {
71
        let mut version_line = String::new();
72

            
73
        self.stream
74
            .read_line(&mut version_line)
75
            .await
76
            .map_err(MpdClientError::ConnectionError)?;
77

            
78
        self.mpd_version = Some(version_line.trim().to_string());
79

            
80
        Ok(())
81
    }
82

            
83
    async fn read_response(&mut self) -> Result<Vec<u8>, MpdClientError> {
84
        let mut response = Vec::new();
85

            
86
        loop {
87
            let mut line = Vec::new();
88

            
89
            let bytes_read = self
90
                .stream
91
                .read_until(b'\n', &mut line)
92
                .await
93
                .map_err(MpdClientError::ConnectionError)?;
94

            
95
            if bytes_read == 0 {
96
                break; // EOF reached
97
            }
98

            
99
            response.extend_from_slice(&line);
100

            
101
            if line == b"OK\n" || line.starts_with(b"ACK ") {
102
                break; // End of response
103
            }
104
        }
105

            
106
        Ok(response)
107
    }
108

            
109
    pub async fn play(
110
        &mut self,
111
        position: Option<SongPosition>,
112
    ) -> Result<PlayResponse, MpdClientError> {
113
        let message = Request::Play(position);
114
        let payload = message.serialize();
115

            
116
        self.stream
117
            .write_all(payload.as_bytes())
118
            .await
119
            .map_err(MpdClientError::ConnectionError)?;
120

            
121
        self.stream
122
            .flush()
123
            .await
124
            .map_err(MpdClientError::ConnectionError)?;
125

            
126
        let response_bytes = self.read_response().await?;
127
        let response = PlayResponse::parse_raw(&response_bytes)?;
128
        Ok(response)
129
    }
130
}