Skip to main content

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