1use std::sync::mpsc::{Receiver, Sender, channel};
2
3use blake3::Hash;
4use selene_core::library::collectable::Collectable;
5use serde::{Deserialize, Serialize, de::DeserializeOwned};
6use thiserror::Error;
7
8use crate::{
9 LoopMode, ShuffleMode,
10 client::{CallbackFn, IpcHandleError, SeleneClient},
11 player::{PlaybackStatus, PlayerQueryFlags, QueryResult},
12};
13
14#[repr(u8)]
15pub(crate) enum PacketType {
16 Unknown,
18
19 Event,
21
22 Response,
24
25 Error,
27
28 Disconnect,
30}
31
32#[derive(Debug, Error, Serialize, Deserialize, Clone, Copy)]
33pub enum PacketError {
34 #[error("Packet size '{size}' too large: Max size is {max_size}")]
35 PacketTooLarge { size: usize, max_size: usize },
36
37 #[error("Client was disconnected while waiting for a packet")]
38 Disconnect,
39}
40
41impl From<u8> for PacketType {
42 fn from(value: u8) -> Self {
43 match value {
44 1 => Self::Event,
45 2 => Self::Response,
46 3 => Self::Error,
47 4 => Self::Disconnect,
48 _ => Self::Unknown,
49 }
50 }
51}
52impl SeleneClient {
53 pub fn ipc_generic(&self, command: IpcCommand) -> Result<(), PacketError> {
55 self.handle_tx.no_response(command)
56 }
57
58 pub fn ipc_flush(&self) -> Result<(), PacketError> {
59 self.handle_tx.request(IpcCommand::Flush)
60 }
61
62 pub fn ipc_disconnect(&self) {
63 self.handle_tx.action(IpcCommand::Disconnect);
64 }
65
66 pub fn ipc_reload_config(&self) -> Result<Result<(), String>, PacketError> {
67 self.handle_tx.request(IpcCommand::ReloadConfig)
68 }
69
70 pub fn ipc_play(&self, collectable: Vec<Collectable>) {
72 self.handle_tx.action(IpcCommand::Play {
73 collectables: collectable,
74 });
75 }
76
77 pub fn ipc_stop(&self) {
78 self.handle_tx.action(IpcCommand::Stop);
79 }
80
81 pub fn ipc_next(&self) {
82 self.handle_tx.action(IpcCommand::Next);
83 }
84
85 pub fn ipc_previous(&self) {
86 self.handle_tx.action(IpcCommand::Previous);
87 }
88
89 pub fn ipc_set_is_playing(&self, is_playing: bool) {
90 self.handle_tx
91 .action(IpcCommand::SetIsPlaying { is_playing });
92 }
93
94 pub fn ipc_toggle_is_playing(&self) -> Result<PlaybackStatus, PacketError> {
95 self.handle_tx.request(IpcCommand::TogglePlaying)
96 }
97
98 pub fn ipc_seek(&self, time: f64, increment: bool) -> Result<Option<f64>, PacketError> {
99 self.handle_tx.request(IpcCommand::Seek {
100 seconds: time,
101 increment,
102 })
103 }
104
105 pub fn ipc_set_volume(&self, volume: f32, increment: bool) -> Result<f32, PacketError> {
106 self.handle_tx
107 .request(IpcCommand::SetVolume { volume, increment })
108 }
109
110 pub fn ipc_queue_set(
112 &self,
113 tracks: Vec<Collectable>,
114 expected_state: Hash,
115 ) -> Result<bool, PacketError> {
116 self.handle_tx.request(IpcCommand::QueueSet {
117 tracks,
118 expected_state,
119 })
120 }
121
122 pub fn ipc_queue_extend(&self, tracks: Vec<Collectable>) {
123 self.handle_tx.action(IpcCommand::QueueExtend(tracks));
124 }
125
126 pub fn ipc_queue_shuffle(&self) {
127 self.handle_tx.action(IpcCommand::QueueShuffle);
128 }
129
130 pub fn ipc_queue_clear(&self) {
131 self.handle_tx.action(IpcCommand::QueueClear);
132 }
133
134 pub fn ipc_playlist_set(
136 &self,
137 collectables: Vec<Collectable>,
138 expected_state: Hash,
139 ) -> Result<bool, PacketError> {
140 self.handle_tx.request(IpcCommand::PlaylistSet {
141 collectables,
142 expected_state,
143 })
144 }
145
146 pub fn ipc_playlist_extend(&self, collectables: Vec<Collectable>) {
147 self.handle_tx
148 .action(IpcCommand::PlaylistExtend(collectables));
149 }
150
151 pub fn ipc_playlist_clear(&self) {
152 self.handle_tx.action(IpcCommand::PlaylistClear);
153 }
154
155 pub fn ipc_playlist_set_shuffle_mode(&self, shuffle_mode: ShuffleMode) {
156 self.handle_tx
157 .action(IpcCommand::PlaylistSetShuffleMode { shuffle_mode });
158 }
159
160 pub fn ipc_playlist_set_loop_mode(&self, loop_mode: LoopMode) {
161 self.handle_tx
162 .action(IpcCommand::PlaylistSetLoopMode { loop_mode });
163 }
164
165 pub fn ipc_tracklist_seek(&self, index: isize, increment: bool) -> Result<usize, PacketError> {
167 self.handle_tx
168 .request(IpcCommand::TracklistSeek { index, increment })
169 }
170
171 pub fn ipc_query(&self, flags: PlayerQueryFlags) -> Result<QueryResult, PacketError> {
173 self.handle_tx.request(IpcCommand::GetState { flags })
174 }
175}
176
177#[derive(Debug, Serialize, Deserialize, Clone)]
178#[non_exhaustive]
179pub enum IpcCommand {
180 Flush,
182 Disconnect,
183 ReloadConfig,
184
185 Play {
187 collectables: Vec<Collectable>,
188 },
189 Stop,
190 SetIsPlaying {
191 is_playing: bool,
192 },
193 TogglePlaying,
194 Seek {
195 seconds: f64,
196 increment: bool,
197 },
198
199 SetVolume {
200 volume: f32,
201 increment: bool,
202 },
203
204 QueueSet {
206 tracks: Vec<Collectable>,
207 expected_state: Hash,
208 },
209 QueueExtend(Vec<Collectable>),
210 QueueShuffle,
211 QueueClear,
212
213 PlaylistSet {
215 collectables: Vec<Collectable>,
216 expected_state: Hash,
217 },
218 PlaylistExtend(Vec<Collectable>),
219 PlaylistClear,
220
221 PlaylistSetShuffleMode {
223 shuffle_mode: ShuffleMode,
224 },
225
226 PlaylistSetLoopMode {
228 loop_mode: LoopMode,
229 },
230
231 TracklistSeek {
233 index: isize,
234 increment: bool,
235 },
236 Next,
237 Previous,
238
239 GetState {
241 flags: PlayerQueryFlags,
242 },
243}
244
245impl IpcCommand {
246 #[must_use]
247 pub fn responds(&self) -> bool {
248 matches!(
249 self,
250 IpcCommand::Flush
251 | IpcCommand::TogglePlaying
252 | IpcCommand::Seek { .. }
253 | IpcCommand::SetVolume { .. }
254 | IpcCommand::QueueSet { .. }
255 | IpcCommand::PlaylistSet { .. }
256 | IpcCommand::TracklistSeek { .. }
257 | IpcCommand::GetState { .. }
258 )
259 }
260}
261
262pub enum IpcRequest {
263 Ipc {
264 command: IpcCommand,
265 callback: Option<CallbackFn>,
266 },
267 Reconnect {
268 callback: Sender<Result<(), IpcHandleError>>,
269 },
270}
271
272pub trait IpcTx {
273 fn no_response(&self, command: IpcCommand) -> Result<(), PacketError>;
274
275 fn request<T: DeserializeOwned + Send + 'static>(
276 &self,
277 command: IpcCommand,
278 ) -> Result<T, PacketError>;
279
280 fn action(&self, command: IpcCommand);
281
282 fn disconnect(&self);
283
284 fn reconnect(&self) -> Result<(), IpcHandleError>;
285}
286
287impl IpcTx for Sender<IpcRequest> {
288 fn no_response(&self, command: IpcCommand) -> Result<(), PacketError> {
289 self.send(IpcRequest::Ipc {
290 command,
291 callback: None,
292 })
293 .unwrap();
294
295 Ok(())
296 }
297
298 fn request<T: DeserializeOwned + Send + 'static>(
299 &self,
300 command: IpcCommand,
301 ) -> Result<T, PacketError> {
302 assert!(
303 command.responds(),
304 "Commands must always respond with request()"
305 );
306
307 let (tx, rx) = channel();
308 let callback: CallbackFn = Box::new(move |result| {
309 let _ = tx.send(
310 result.map(|bytes| postcard::from_bytes(bytes).expect("Daemon sent invalid bytes")),
311 );
312 });
313
314 self.send(IpcRequest::Ipc {
315 command,
316 callback: Some(Box::new(callback)),
317 })
318 .unwrap();
319
320 rx.recv().unwrap()
321 }
322
323 fn action(&self, command: IpcCommand) {
324 assert!(
325 !command.responds(),
326 "Commands must never respond with action()"
327 );
328
329 self.send(IpcRequest::Ipc {
330 command,
331 callback: None,
332 })
333 .unwrap();
334 }
335
336 fn disconnect(&self) {
337 let _ = self.send(IpcRequest::Ipc {
338 command: IpcCommand::Disconnect,
339 callback: None,
340 });
341 }
342
343 fn reconnect(&self) -> Result<(), IpcHandleError> {
344 let (tx, rx) = channel();
345 self.send(IpcRequest::Reconnect { callback: tx })
346 .map_err(|_| IpcHandleError::HandleDied)?;
347 rx.recv().map_err(|_| IpcHandleError::HandleDied)?
348 }
349}
350
351pub(crate) trait IpcRx {
352 fn wait_for_reconnect_request(&self) -> Option<Sender<Result<(), IpcHandleError>>>;
353}
354
355impl IpcRx for Receiver<IpcRequest> {
356 fn wait_for_reconnect_request(&self) -> Option<Sender<Result<(), IpcHandleError>>> {
357 loop {
358 match self.recv() {
359 Ok(IpcRequest::Ipc { callback, .. }) => {
360 if let Some(callback) = callback {
361 callback(Err(PacketError::Disconnect));
362 }
363
364 continue;
365 }
366 Ok(IpcRequest::Reconnect { callback }) => return Some(callback),
367 Err(_) => return None,
368 }
369 }
370 }
371}