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