1use std::sync::Arc;
2
3use action::Action;
4use mecomp_core::{
5 rpc::{MusicPlayerClient, SearchResult},
6 state::{StateAudio, library::LibraryBrief},
7};
8use tokio::sync::{
9 broadcast,
10 mpsc::{self, UnboundedReceiver, UnboundedSender},
11};
12
13use crate::{
14 termination::{Interrupted, Terminator},
15 ui::{components::content_view::ActiveView, widgets::popups::PopupType},
16};
17
18pub mod action;
19pub mod audio;
20pub mod component;
21pub mod library;
22pub mod popup;
23pub mod search;
24pub mod view;
25
26pub struct Dispatcher {
28 audio: audio::AudioState,
29 search: search::SearchState,
30 library: library::LibraryState,
31 view: view::ViewState,
32 popup: popup::PopupState,
33 component: component::ComponentState,
34}
35
36struct Senders {
38 pub audio: UnboundedSender<action::AudioAction>,
39 pub search: UnboundedSender<String>,
40 pub library: UnboundedSender<action::LibraryAction>,
41 pub view: UnboundedSender<action::ViewAction>,
42 pub popup: UnboundedSender<action::PopupAction>,
43 pub component: UnboundedSender<action::ComponentAction>,
44}
45
46pub struct Receivers {
48 pub audio: UnboundedReceiver<StateAudio>,
49 pub search: UnboundedReceiver<SearchResult>,
50 pub library: UnboundedReceiver<LibraryBrief>,
51 pub view: UnboundedReceiver<ActiveView>,
52 pub popup: UnboundedReceiver<Option<PopupType>>,
53 pub component: UnboundedReceiver<component::ActiveComponent>,
54}
55
56impl Dispatcher {
57 #[must_use]
58 pub fn new() -> (Self, Receivers) {
59 let (audio, audio_rx) = audio::AudioState::new();
60 let (search, search_rx) = search::SearchState::new();
61 let (library, library_rx) = library::LibraryState::new();
62 let (view, view_rx) = view::ViewState::new();
63 let (popup, popup_rx) = popup::PopupState::new();
64 let (active_component, active_component_rx) = component::ComponentState::new();
65
66 let dispatcher = Self {
67 audio,
68 search,
69 library,
70 view,
71 popup,
72 component: active_component,
73 };
74 let state_receivers = Receivers {
75 audio: audio_rx,
76 search: search_rx,
77 library: library_rx,
78 view: view_rx,
79 popup: popup_rx,
80 component: active_component_rx,
81 };
82
83 (dispatcher, state_receivers)
84 }
85
86 pub async fn main_loop(
94 &self,
95 daemon: Arc<MusicPlayerClient>,
96 terminator: Terminator,
97 action_rx: UnboundedReceiver<Action>,
98 mut interrupt_rx: broadcast::Receiver<Interrupted>,
99 ) -> anyhow::Result<Interrupted> {
100 let (audio_action_tx, audio_action_rx) = mpsc::unbounded_channel();
101 let (search_action_tx, search_action_rx) = mpsc::unbounded_channel();
102 let (library_action_tx, library_action_rx) = mpsc::unbounded_channel();
103 let (view_action_tx, view_action_rx) = mpsc::unbounded_channel();
104 let (popup_action_tx, popup_action_rx) = mpsc::unbounded_channel();
105 let (component_action_tx, component_action_rx) = mpsc::unbounded_channel();
106
107 tokio::try_join!(
113 self.audio
115 .main_loop(daemon.clone(), audio_action_rx, interrupt_rx.resubscribe()),
116 self.search
118 .main_loop(daemon.clone(), search_action_rx, interrupt_rx.resubscribe()),
119 self.library.main_loop(
121 daemon.clone(),
122 library_action_rx,
123 interrupt_rx.resubscribe()
124 ),
125 self.view
127 .main_loop(view_action_rx, interrupt_rx.resubscribe()),
128 self.popup
130 .main_loop(popup_action_rx, interrupt_rx.resubscribe()),
131 self.component
133 .main_loop(component_action_rx, interrupt_rx.resubscribe()),
134 Self::action_dispatcher(
136 terminator,
137 action_rx,
138 Senders {
139 audio: audio_action_tx,
140 search: search_action_tx,
141 library: library_action_tx,
142 view: view_action_tx,
143 popup: popup_action_tx,
144 component: component_action_tx,
145 },
146 ),
147 )?;
148
149 Ok(interrupt_rx.recv().await?)
150 }
151
152 async fn action_dispatcher(
153 mut terminator: Terminator,
154 mut action_rx: UnboundedReceiver<Action>,
155 senders: Senders,
156 ) -> anyhow::Result<()> {
157 while let Some(action) = action_rx.recv().await {
158 match action {
159 Action::Audio(action) => {
160 senders.audio.send(action)?;
161 }
162 Action::Search(query) => {
163 senders.search.send(query)?;
164 }
165 Action::General(action) => match action {
166 action::GeneralAction::Exit => {
167 let _ = terminator.terminate(Interrupted::UserInt);
168
169 break;
170 }
171 },
172 Action::Library(action) => {
173 senders.library.send(action)?;
174 }
175 Action::ActiveView(action) => {
176 senders.view.send(action)?;
177 }
178 Action::Popup(popup) => senders.popup.send(popup)?,
179 Action::ActiveComponent(action) => {
180 senders.component.send(action)?;
181 }
182 }
183 }
184
185 Ok(())
186 }
187}