mecomp_tui/state/
search.rs

1//! This module contains the implementation of audio state store.
2//! which is updated every tick and used by views to render the audio playback and queue state.
3//!
4//! The audio state store is responsible for maintaining the audio state, and for handling audio related actions.
5
6use std::sync::Arc;
7
8use tokio::sync::{
9    broadcast,
10    mpsc::{UnboundedReceiver, UnboundedSender, unbounded_channel},
11};
12
13use mecomp_core::rpc::{MusicPlayerClient, SearchResult};
14
15use crate::termination::Interrupted;
16
17/// The audio state store.
18#[derive(Debug, Clone)]
19#[allow(clippy::module_name_repetitions)]
20pub struct SearchState {
21    state_tx: UnboundedSender<SearchResult>,
22}
23
24impl SearchState {
25    /// create a new audio state store, and return the receiver for listening to state updates.
26    #[must_use]
27    pub fn new() -> (Self, UnboundedReceiver<SearchResult>) {
28        let (state_tx, state_rx) = unbounded_channel::<SearchResult>();
29
30        (Self { state_tx }, state_rx)
31    }
32
33    /// a loop that updates the audio state every tick.
34    ///
35    /// # Errors
36    ///
37    /// Fails if the state cannot be sent
38    /// or if the daemon client can't connect to the server
39    pub async fn main_loop(
40        &self,
41        daemon: Arc<MusicPlayerClient>,
42        mut action_rx: UnboundedReceiver<String>,
43        mut interrupt_rx: broadcast::Receiver<Interrupted>,
44    ) -> anyhow::Result<Interrupted> {
45        let mut state = SearchResult::default();
46
47        // the initial state once
48        self.state_tx.send(state.clone())?;
49
50        let result = loop {
51            tokio::select! {
52                // Handle the actions coming from the UI
53                // and process them to do async operations
54                Some(query) = action_rx.recv() => {
55                    let ctx = tarpc::context::current();
56                    state = daemon.search(ctx, query, 100).await?;
57                    self.state_tx.send(state.clone())?;
58                },
59                // Catch and handle interrupt signal to gracefully shutdown
60                Ok(interrupted) = interrupt_rx.recv() => {
61                    break interrupted;
62                }
63            }
64        };
65
66        Ok(result)
67    }
68}