async_mpd/client/
mpdclient.rs

1use async_net::{AsyncToSocketAddrs, TcpStream};
2use futures_lite::{io::BufReader, AsyncWriteExt};
3use std::net::SocketAddr;
4
5use crate::resp::WrappedResponse;
6use crate::{
7    client::resp::{
8        handlers::ResponseHandler,
9        read_resp_line,
10        respmap_handlers::{ListallResponse, ListallinfoResponse},
11    },
12    cmd::{self, MpdCmd},
13    DatabaseVersion, Error, Filter, Stats, Status, Subsystem, Track,
14};
15
16/// Mpd Client
17#[derive(Default)]
18pub struct MpdClient {
19    /// Buffered Stream
20    stream: Option<BufReader<TcpStream>>,
21    // Addr
22    addr: Option<SocketAddr>,
23}
24
25impl MpdClient {
26    /// Create a new MpdClient
27    pub fn new() -> Self {
28        Self {
29            stream: None,
30            addr: None,
31        }
32    }
33
34    pub async fn connect<A: AsyncToSocketAddrs>(&mut self, addr: A) -> Result<String, Error> {
35        let stream = TcpStream::connect(addr).await?;
36        // Save the resolved adress for reconnect
37        let sock_addr = stream.peer_addr()?;
38
39        let reader = BufReader::new(stream);
40
41        log::debug!("server: {:?}", sock_addr);
42
43        self.stream = Some(reader);
44        self.addr = Some(sock_addr);
45
46        // After connect, the server replies with a a version reply
47        Ok(self.read_version().await?)
48    }
49
50    pub async fn reconnect(&mut self) -> Result<(), Error> {
51        if let Some(addr) = self.addr {
52            log::debug!("Reconnection to: {:?}", addr);
53            self.connect(addr).await.map(|_| ())
54        } else {
55            log::warn!("Reconnect without previous connection");
56            Err(Error::Disconnected)
57        }
58    }
59
60    async fn read_version(&mut self) -> Result<String, Error> {
61        let br = self.stream.as_mut().ok_or(Error::Disconnected)?;
62
63        let version = read_resp_line(br).await?;
64        log::debug!("Connected: {}", version);
65        Ok(version)
66    }
67
68    /// Get stats on the music database
69    pub async fn stats(&mut self) -> Result<Stats, Error> {
70        self.exec(cmd::Stats).await
71    }
72
73    pub async fn status(&mut self) -> Result<Status, Error> {
74        let status = self.exec(cmd::Status).await?;
75        Ok(status)
76    }
77
78    pub async fn update(&mut self, path: Option<&str>) -> Result<DatabaseVersion, Error> {
79        self.exec(cmd::Update(path)).await
80    }
81
82    pub async fn rescan(&mut self, path: Option<&str>) -> Result<DatabaseVersion, Error> {
83        self.exec(cmd::Rescan(path)).await
84    }
85
86    pub async fn idle(&mut self) -> Result<Subsystem, Error> {
87        self.exec(cmd::Idle).await
88    }
89
90    pub async fn noidle(&mut self) -> Result<(), Error> {
91        self.exec(cmd::NoIdle).await
92    }
93
94    pub async fn setvol(&mut self, volume: u32) -> Result<(), Error> {
95        self.exec(cmd::Setvol(volume)).await
96    }
97
98    pub async fn repeat(&mut self, repeat: bool) -> Result<(), Error> {
99        self.exec(cmd::Repeat(repeat)).await
100    }
101
102    pub async fn random(&mut self, random: bool) -> Result<(), Error> {
103        self.exec(cmd::Random(random)).await
104    }
105
106    pub async fn consume(&mut self, consume: bool) -> Result<(), Error> {
107        self.exec(cmd::Consume(consume)).await
108    }
109
110    // Playback controls
111
112    pub async fn play(&mut self) -> Result<(), Error> {
113        self.play_pause(true).await
114    }
115
116    pub async fn playid(&mut self, id: u32) -> Result<(), Error> {
117        self.exec(cmd::PlayId(id)).await
118    }
119
120    pub async fn pause(&mut self) -> Result<(), Error> {
121        self.play_pause(false).await
122    }
123
124    pub async fn play_pause(&mut self, play: bool) -> Result<(), Error> {
125        self.exec(cmd::PlayPause(!play)).await
126    }
127
128    pub async fn next(&mut self) -> Result<(), Error> {
129        self.exec(cmd::Next).await
130    }
131
132    pub async fn prev(&mut self) -> Result<(), Error> {
133        self.exec(cmd::Prev).await
134    }
135
136    pub async fn stop(&mut self) -> Result<(), Error> {
137        self.exec(cmd::Stop).await
138    }
139
140    //
141    // Music database commands
142    //
143
144    pub async fn listall(&mut self, path: Option<&str>) -> Result<ListallResponse, Error> {
145        self.exec(cmd::Listall(path)).await
146    }
147
148    pub async fn listallinfo(&mut self, path: Option<&str>) -> Result<ListallinfoResponse, Error> {
149        self.exec(cmd::ListallInfo(path)).await
150    }
151
152    // Queue handling commands
153
154    pub async fn queue_add(&mut self, path: &str) -> Result<(), Error> {
155        self.exec(cmd::QueueAdd(path)).await
156    }
157
158    pub async fn queue_clear(&mut self) -> Result<(), Error> {
159        self.exec(cmd::QueueClear).await
160    }
161
162    pub async fn queue(&mut self) -> Result<Vec<Track>, Error> {
163        self.exec(cmd::PlaylistInfo).await
164    }
165
166    /// # Example
167    /// ```
168    /// use async_mpd::{MpdClient, Error, Tag, Filter, ToFilterExpr};
169    ///
170    /// #[async_std::main]
171    /// async fn main() -> Result<(), Error> {
172    ///     // Connect to server
173    ///     let mut mpd = MpdClient::new();
174    ///     mpd.connect("localhost:6600").await?;
175    ///
176    ///     let mut filter = Filter::new()
177    ///         .and(Tag::Artist.equals("The Beatles"))
178    ///         .and(Tag::Album.contains("White"));
179    ///
180    ///     let res = mpd.search(&filter).await?;
181    ///     println!("{:?}", res);
182    ///
183    ///     Ok(())
184    /// }
185    /// ```
186    pub async fn search(&mut self, filter: &Filter) -> Result<Vec<Track>, Error> {
187        self.exec(cmd::Search(filter.to_query().as_deref())).await
188    }
189
190    /// Execute a Mpd Command. Returns a enum wrapped Response
191    pub async fn exec_wrapped<C>(&mut self, cmd: C) -> Result<WrappedResponse, crate::Error>
192    where
193        C: MpdCmd,
194    {
195        self.exec(cmd).await.map(Into::into)
196    }
197
198    /// Execute a Mpd Command, get back the matching Response
199    pub async fn exec<C>(
200        &mut self,
201        cmd: C,
202    ) -> Result<<C::Handler as ResponseHandler>::Response, crate::Error>
203    where
204        C: MpdCmd,
205    {
206        let cmdline = cmd.to_cmdline();
207
208        self.send_command(&cmdline).await?;
209
210        let br = self.stream.as_mut().ok_or(Error::Disconnected)?;
211
212        // Handle the response associated with this command
213        C::Handler::handle(br).await
214    }
215
216    async fn send_command(&mut self, line: &str) -> Result<(), crate::Error> {
217        // Get the underlying TcpStream and write command to the socket
218        self.stream
219            .as_mut()
220            .ok_or(crate::Error::Disconnected)?
221            .get_mut()
222            .write_all(line.as_bytes())
223            .await
224            .map_err(|_| crate::Error::Disconnected)?;
225
226        Ok(())
227    }
228}