1use crate::model::app::AppContext;
2use crate::model::muxbox::Choice;
3use crate::{run_script, run_script_with_pty_and_redirect, FieldUpdate, MuxBox, Updatable};
4use bincode;
5use log::error;
6use std::collections::hash_map::DefaultHasher;
7use std::collections::HashMap;
8use std::hash::{Hash, Hasher};
9use std::sync::mpsc::{self, Sender};
10use std::thread;
11use uuid::Uuid;
12
13#[derive(Debug, Clone, PartialEq, Eq)]
14pub enum Message {
15 Exit,
16 Terminate,
17 Pause,
18 Start,
19 NextMuxBox(),
20 PreviousMuxBox(),
21 ScrollMuxBoxDown(),
22 ScrollMuxBoxUp(),
23 ScrollMuxBoxLeft(),
24 ScrollMuxBoxRight(),
25 ScrollMuxBoxPageUp(),
26 ScrollMuxBoxPageDown(),
27 ScrollMuxBoxPageLeft(),
28 ScrollMuxBoxPageRight(),
29 ScrollMuxBoxToBeginning(), ScrollMuxBoxToEnd(), ScrollMuxBoxToTop(), ScrollMuxBoxToBottom(), CopyFocusedMuxBoxContent(),
34 Resize,
35 RedrawMuxBox(String),
36 RedrawApp,
37 RedrawAppDiff, MuxBoxEventRefresh(String),
39 MuxBoxOutputUpdate(String, bool, String),
40 MuxBoxScriptUpdate(String, Vec<String>),
41 ReplaceMuxBox(String, MuxBox),
42 StopBoxRefresh(String),
43 StartBoxRefresh(String),
44 SwitchActiveLayout(String),
45 KeyPress(String),
46 ExecuteHotKeyChoice(String),
47 MouseClick(u16, u16), MouseDragStart(u16, u16), MouseDrag(u16, u16), MouseDragEnd(u16, u16), MuxBoxBorderDrag(String, u16, u16), MuxBoxResizeComplete(String), MuxBoxMove(String, u16, u16), MuxBoxMoveComplete(String), SaveYamlState, SaveActiveLayout(String), SaveMuxBoxContent(String, String), SaveMuxBoxScroll(String, usize, usize), PTYInput(String, String), ExecuteChoice(Choice, String, Option<Vec<String>>), ChoiceExecutionComplete(String, String, Result<String, String>), ExternalMessage(String),
63 AddBox(String, MuxBox),
64 RemoveBox(String),
65}
66
67impl Hash for Message {
68 fn hash<H: Hasher>(&self, state: &mut H) {
69 match self {
70 Message::Exit => "exit".hash(state),
71 Message::Terminate => "terminate".hash(state),
72 Message::NextMuxBox() => "next_muxbox".hash(state),
73 Message::PreviousMuxBox() => "previous_muxbox".hash(state),
74 Message::Resize => "resize".hash(state),
75 Message::RedrawMuxBox(muxbox_id) => {
76 "redraw_muxbox".hash(state);
77 muxbox_id.hash(state);
78 }
79 Message::RedrawApp => "redraw_app".hash(state),
80 Message::RedrawAppDiff => "redraw_app_diff".hash(state),
81 Message::SwitchActiveLayout(layout_id) => {
82 "switch_active_layout".hash(state);
83 layout_id.hash(state);
84 }
85 Message::MuxBoxEventRefresh(muxbox_id) => {
86 "muxbox_event_refresh".hash(state);
87 muxbox_id.hash(state);
88 }
89 Message::ScrollMuxBoxDown() => "scroll_muxbox_down".hash(state),
90 Message::ScrollMuxBoxUp() => "scroll_muxbox_up".hash(state),
91 Message::ScrollMuxBoxLeft() => "scroll_muxbox_left".hash(state),
92 Message::ScrollMuxBoxRight() => "scroll_muxbox_right".hash(state),
93 Message::ScrollMuxBoxPageUp() => "scroll_muxbox_page_up".hash(state),
94 Message::ScrollMuxBoxPageDown() => "scroll_muxbox_page_down".hash(state),
95 Message::ScrollMuxBoxPageLeft() => "scroll_muxbox_page_left".hash(state),
96 Message::ScrollMuxBoxPageRight() => "scroll_muxbox_page_right".hash(state),
97 Message::ScrollMuxBoxToBeginning() => "scroll_muxbox_to_beginning".hash(state),
98 Message::ScrollMuxBoxToEnd() => "scroll_muxbox_to_end".hash(state),
99 Message::ScrollMuxBoxToTop() => "scroll_muxbox_to_top".hash(state),
100 Message::ScrollMuxBoxToBottom() => "scroll_muxbox_to_bottom".hash(state),
101 Message::CopyFocusedMuxBoxContent() => "copy_focused_muxbox_content".hash(state),
102 Message::MuxBoxOutputUpdate(muxbox_id, success, output) => {
103 "muxbox_output_update".hash(state);
104 muxbox_id.hash(state);
105 success.hash(state);
106 output.hash(state);
107 }
108 Message::MuxBoxScriptUpdate(muxbox_id, script) => {
109 "muxbox_script_update".hash(state);
110 muxbox_id.hash(state);
111 script.hash(state);
112 }
113 Message::ReplaceMuxBox(muxbox_id, muxbox) => {
114 "replace_muxbox".hash(state);
115 muxbox_id.hash(state);
116 muxbox.hash(state);
117 }
118 Message::KeyPress(pressed_key) => {
119 "key_press".hash(state);
120 pressed_key.hash(state);
121 }
122 Message::ExecuteHotKeyChoice(choice_id) => {
123 "execute_hot_key_choice".hash(state);
124 choice_id.hash(state);
125 }
126 Message::MouseClick(x, y) => {
127 "mouse_click".hash(state);
128 x.hash(state);
129 y.hash(state);
130 }
131 Message::MouseDragStart(x, y) => {
132 "mouse_drag_start".hash(state);
133 x.hash(state);
134 y.hash(state);
135 }
136 Message::MouseDrag(x, y) => {
137 "mouse_drag".hash(state);
138 x.hash(state);
139 y.hash(state);
140 }
141 Message::MouseDragEnd(x, y) => {
142 "mouse_drag_end".hash(state);
143 x.hash(state);
144 y.hash(state);
145 }
146 Message::MuxBoxBorderDrag(muxbox_id, x, y) => {
147 "muxbox_border_drag".hash(state);
148 muxbox_id.hash(state);
149 x.hash(state);
150 y.hash(state);
151 }
152 Message::MuxBoxResizeComplete(muxbox_id) => {
153 "muxbox_resize_complete".hash(state);
154 muxbox_id.hash(state);
155 }
156 Message::MuxBoxMove(muxbox_id, x, y) => {
157 "muxbox_move".hash(state);
158 muxbox_id.hash(state);
159 x.hash(state);
160 y.hash(state);
161 }
162 Message::MuxBoxMoveComplete(muxbox_id) => {
163 "muxbox_move_complete".hash(state);
164 muxbox_id.hash(state);
165 }
166 Message::SaveYamlState => "save_yaml_state".hash(state),
167 Message::SaveActiveLayout(layout_id) => {
168 "save_active_layout".hash(state);
169 layout_id.hash(state);
170 }
171 Message::SaveMuxBoxContent(muxbox_id, content) => {
172 "save_muxbox_content".hash(state);
173 muxbox_id.hash(state);
174 content.hash(state);
175 }
176 Message::SaveMuxBoxScroll(muxbox_id, x, y) => {
177 "save_muxbox_scroll".hash(state);
178 muxbox_id.hash(state);
179 x.hash(state);
180 y.hash(state);
181 }
182 Message::PTYInput(muxbox_id, input) => {
183 "pty_input".hash(state);
184 muxbox_id.hash(state);
185 input.hash(state);
186 }
187 Message::ExecuteChoice(choice, muxbox_id, libs) => {
188 "execute_choice".hash(state);
189 choice.hash(state);
190 muxbox_id.hash(state);
191 libs.hash(state);
192 }
193 Message::ChoiceExecutionComplete(choice_id, muxbox_id, result) => {
194 "choice_execution_complete".hash(state);
195 choice_id.hash(state);
196 muxbox_id.hash(state);
197 match result {
198 Ok(output) => {
199 "ok".hash(state);
200 output.hash(state);
201 }
202 Err(error) => {
203 "err".hash(state);
204 error.hash(state);
205 }
206 }
207 }
208 Message::Pause => "pause".hash(state),
209 Message::ExternalMessage(msg) => {
210 "external_message".hash(state);
211 msg.hash(state);
212 }
213 Message::Start => "start".hash(state),
214 Message::StopBoxRefresh(box_id) => {
215 "stop_box_refresh".hash(state);
216 box_id.hash(state);
217 }
218 Message::StartBoxRefresh(box_id) => {
219 "start_box_refresh".hash(state);
220 box_id.hash(state);
221 }
222 Message::AddBox(box_id, muxbox) => {
223 "add_box".hash(state);
224 box_id.hash(state);
225 muxbox.hash(state);
226 }
227 Message::RemoveBox(box_id) => {
228 "remove_box".hash(state);
229 box_id.hash(state);
230 }
231 }
232 }
233}
234
235pub trait Runnable: Send + 'static {
236 fn run(&mut self) -> Result<bool, Box<dyn std::error::Error>>;
237 fn receive_updates(&mut self) -> (AppContext, Vec<Message>);
238 fn process(&mut self, app_context: AppContext, messages: Vec<Message>);
239
240 fn update_app_context(&mut self, app_context: AppContext);
241 fn set_uuid(&mut self, uuid: Uuid);
242 fn get_uuid(&self) -> Uuid;
243 fn set_app_context_sender(
244 &mut self,
245 app_context_sender: mpsc::Sender<(Uuid, Vec<FieldUpdate>)>,
246 );
247 fn set_message_sender(&mut self, message_sender: mpsc::Sender<(Uuid, Message)>);
248 fn set_app_context_receiver(
249 &mut self,
250 app_context_receiver: mpsc::Receiver<(Uuid, Vec<FieldUpdate>)>,
251 );
252 fn set_message_receiver(&mut self, message_receiver: mpsc::Receiver<(Uuid, Message)>);
253 fn get_app_context(&self) -> &AppContext;
254 fn get_app_context_sender(&self) -> &Option<mpsc::Sender<(Uuid, Vec<FieldUpdate>)>>;
255 fn get_message_sender(&self) -> &Option<mpsc::Sender<(Uuid, Message)>>;
256
257 fn send_app_context_update(&self, old_app_context: AppContext);
258 fn send_message(&self, msg: Message);
259}
260
261#[derive(Debug, Clone, PartialEq, Eq)]
262pub enum RunnableState {
263 Created,
264 Running,
265 Paused,
266 Terminated,
267}
268
269pub struct RunnableImpl {
270 pub app_context: AppContext,
271 uuid: Uuid,
272 running_state: RunnableState,
273 app_context_sender: Option<mpsc::Sender<(Uuid, Vec<FieldUpdate>)>>,
274 message_sender: Option<mpsc::Sender<(Uuid, Message)>>,
275 app_context_receiver: Option<mpsc::Receiver<(Uuid, Vec<FieldUpdate>)>>,
276 message_receiver: Option<mpsc::Receiver<(Uuid, Message)>>,
277}
278
279impl RunnableImpl {
280 pub fn new(app_context: AppContext) -> Self {
281 RunnableImpl {
282 app_context,
283 uuid: Uuid::new_v4(),
284 running_state: RunnableState::Created,
285 app_context_sender: None,
286 message_sender: None,
287 app_context_receiver: None,
288 message_receiver: None,
289 }
290 }
291
292 pub fn _run(
293 &mut self,
294 process_fn: &mut dyn FnMut(&mut Self, AppContext, Vec<Message>) -> (bool, AppContext),
295 ) -> Result<bool, Box<dyn std::error::Error>> {
296 let (updated_app_context, new_messages) = self.receive_updates();
297 let original_app_context = updated_app_context.clone();
298 let mut should_continue = true;
299 for message in new_messages.iter() {
300 match message {
301 Message::Exit => {
302 self.running_state = RunnableState::Terminated;
303 return Ok(false);
304 }
305 Message::Terminate => {
306 self.running_state = RunnableState::Terminated;
307 return Ok(false);
308 }
309 Message::Pause => {
310 self.running_state = RunnableState::Paused;
311 }
312 Message::Start => {
313 self.running_state = RunnableState::Running;
314 }
315 _ => {}
316 }
317 }
318
319 if updated_app_context != self.app_context {
321 self.app_context = updated_app_context;
322 }
323
324 log::info!(
325 "RunnableImpl _run: state={:?}, messages={}",
326 self.running_state,
327 new_messages.len()
328 );
329 if self.running_state == RunnableState::Running {
330 log::info!(
331 "RunnableImpl calling process function with {} messages",
332 new_messages.len()
333 );
334 let (process_should_continue, result_app_context) =
335 process_fn(self, self.app_context.clone(), new_messages);
336 if result_app_context != original_app_context {
337 self.app_context = result_app_context;
338 self.send_app_context_update(original_app_context);
339 }
340 should_continue = process_should_continue;
341 log::info!(
342 "RunnableImpl process function returned should_continue={}",
343 should_continue
344 );
345 } else {
346 log::info!(
347 "RunnableImpl NOT calling process function - state is {:?}",
348 self.running_state
349 );
350 }
351
352 if !should_continue {
353 return Ok(false);
354 }
355 Ok(true)
356 }
357}
358
359impl Runnable for RunnableImpl {
360 fn receive_updates(&mut self) -> (AppContext, Vec<Message>) {
361 let mut app_context_updates = Vec::new();
362 let mut new_messages = Vec::new();
363
364 if let Some(ref app_context_receiver) = self.app_context_receiver {
365 while let Ok((_, received_field_updates)) = app_context_receiver.try_recv() {
366 if !received_field_updates.is_empty() {
367 log::trace!(
368 "Received app_context update: {:?} in thread {}",
369 received_field_updates,
370 self.uuid
371 );
372 app_context_updates = received_field_updates;
373 }
374 }
375 }
376
377 let mut updated_app_context = self.app_context.clone();
378 updated_app_context.apply_updates(app_context_updates);
379
380 if let Some(ref message_receiver) = self.message_receiver {
381 while let Ok((_, message)) = message_receiver.try_recv() {
382 new_messages.push(message);
383 }
384 }
385
386 (updated_app_context, new_messages)
387 }
388
389 fn process(&mut self, app_context: AppContext, messages: Vec<Message>) {
390 self.update_app_context(app_context);
392
393 for message in messages {
395 match message {
396 Message::Terminate => {
397 self.running_state = RunnableState::Terminated;
398 }
399 Message::Pause => {
400 self.running_state = RunnableState::Paused;
401 }
402 Message::Start => {
403 self.running_state = RunnableState::Running;
404 }
405 _ => {
406 }
408 }
409 }
410 }
411
412 fn update_app_context(&mut self, app_context: AppContext) {
413 let old_app_context = self.app_context.clone();
414 self.app_context = app_context;
415 self.send_app_context_update(old_app_context);
416 }
417
418 fn set_uuid(&mut self, uuid: Uuid) {
419 self.uuid = uuid;
420 }
421
422 fn get_uuid(&self) -> Uuid {
423 self.uuid
424 }
425
426 fn set_app_context_sender(&mut self, app_context_sender: Sender<(Uuid, Vec<FieldUpdate>)>) {
427 self.app_context_sender = Some(app_context_sender);
428 }
429
430 fn set_message_sender(&mut self, message_sender: mpsc::Sender<(Uuid, Message)>) {
431 self.message_sender = Some(message_sender);
432 }
433
434 fn set_app_context_receiver(
435 &mut self,
436 app_context_receiver: mpsc::Receiver<(Uuid, Vec<FieldUpdate>)>,
437 ) {
438 self.app_context_receiver = Some(app_context_receiver);
439 }
440
441 fn set_message_receiver(&mut self, message_receiver: mpsc::Receiver<(Uuid, Message)>) {
442 self.message_receiver = Some(message_receiver);
443 }
444
445 fn get_app_context(&self) -> &AppContext {
446 &self.app_context
447 }
448
449 fn get_app_context_sender(&self) -> &Option<mpsc::Sender<(Uuid, Vec<FieldUpdate>)>> {
450 &self.app_context_sender
451 }
452
453 fn get_message_sender(&self) -> &Option<mpsc::Sender<(Uuid, Message)>> {
454 &self.message_sender
455 }
456
457 fn send_app_context_update(&self, old_app_context: AppContext) {
458 if let Some(ref app_context_sender) = self.get_app_context_sender() {
459 if let Err(e) = app_context_sender.send((
460 self.get_uuid(),
461 self.get_app_context().generate_diff(&old_app_context),
462 )) {
463 error!("Failed to send update to main thread: {}", e);
464 }
465 }
466 }
467
468 fn send_message(&self, msg: Message) {
469 if let Some(ref message_sender) = self.get_message_sender() {
470 if let Err(e) = message_sender.send((self.get_uuid(), msg)) {
471 error!("Failed to send message to main thread: {}", e);
472 }
473 }
474 }
475
476 fn run(&mut self) -> Result<bool, Box<dyn std::error::Error>> {
477 todo!()
478 }
479}
480
481#[derive(Debug)]
482pub struct ThreadManager {
483 threads: HashMap<Uuid, thread::JoinHandle<()>>,
484 app_context_senders: HashMap<Uuid, mpsc::Sender<(Uuid, Vec<FieldUpdate>)>>,
485 app_context_receivers: HashMap<Uuid, mpsc::Receiver<(Uuid, Vec<FieldUpdate>)>>,
486 message_senders: HashMap<Uuid, mpsc::Sender<(Uuid, Message)>>,
487 message_receivers: HashMap<Uuid, mpsc::Receiver<(Uuid, Message)>>,
488 app_context: AppContext,
489}
490
491impl ThreadManager {
492 pub fn new(app_context: AppContext) -> Self {
493 ThreadManager {
494 threads: HashMap::new(),
495 app_context_senders: HashMap::new(),
496 message_senders: HashMap::new(),
497 app_context_receivers: HashMap::new(),
498 message_receivers: HashMap::new(),
499 app_context,
500 }
501 }
502
503 pub fn stop(&self) {
504 self.send_message_to_all_threads((Uuid::new_v4(), Message::Exit));
505 }
506
507 pub fn pause(&self) {
508 self.send_message_to_all_threads((Uuid::new_v4(), Message::Pause));
509 }
510
511 pub fn run(&mut self) {
512 for message_sender in self.message_senders.values() {
513 if let Err(e) = message_sender.send((Uuid::new_v4(), Message::Start)) {
514 error!("Failed to send start message to thread: {}", e);
515 }
516 }
517 let mut should_continue: bool = true;
518 while should_continue {
519 let mut has_updates = false;
520
521 for reciever in self.app_context_receivers.values() {
523 if let Ok((uuid, app_context_updates)) = reciever.try_recv() {
524 if app_context_updates.is_empty() {
525 continue;
527 } else {
528 let app_context_updates_size_in_bytes =
529 bincode::serialize(&app_context_updates)
530 .unwrap_or_default()
531 .len();
532 log::trace!(
533 "Received {} updates from thread {} with total size {} bytes. Will relay to all other threads.",
534 app_context_updates.len(),
535 uuid,
536 app_context_updates_size_in_bytes
537 );
538 }
539
540 let original_app_context = self.app_context.clone();
541
542 self.app_context.app.apply_updates(app_context_updates);
547 self.send_app_context_update_to_all_threads((
548 uuid,
549 self.app_context.generate_diff(&original_app_context),
550 ));
551 has_updates = true;
552 }
553 }
554
555 let mut messages_to_process = Vec::new();
557 for reciever in self.message_receivers.values() {
558 if let Ok((uuid, received_msg)) = reciever.try_recv() {
559 messages_to_process.push((uuid, received_msg));
560 }
561 }
562
563 for (uuid, received_msg) in messages_to_process {
565 match received_msg {
567 Message::Exit => {
568 self.send_message_to_all_threads((Uuid::new_v4(), Message::Terminate));
569 should_continue = false;
570 }
571 Message::ExecuteChoice(choice, muxbox_id, libs) => {
572 log::info!(
574 "ThreadManager spawning choice execution: {} on muxbox {}",
575 choice.id,
576 muxbox_id
577 );
578 let choice_runnable =
579 ChoiceExecutionRunnable::new(self.app_context.clone());
580 let choice_uuid = self.spawn_thread(choice_runnable);
581
582 log::info!(
584 "ThreadManager sending ExecuteChoice message to runnable thread: {}",
585 choice_uuid
586 );
587 self.send_message_to_thread(
588 (
589 Uuid::new_v4(),
590 Message::ExecuteChoice(choice, muxbox_id, libs),
591 ),
592 choice_uuid,
593 );
594 has_updates = true;
595 }
596 Message::ChoiceExecutionComplete(ref choice_id, ref muxbox_id, ref result) => {
597 log::info!("ThreadManager received ChoiceExecutionComplete for choice: {} on muxbox: {}", choice_id, muxbox_id);
598 match result {
599 Ok(output) => log::info!(
600 "ThreadManager broadcasting choice success: {} chars of output",
601 output.len()
602 ),
603 Err(error) => {
604 log::error!("ThreadManager broadcasting choice error: {}", error)
605 }
606 }
607 self.send_message_to_all_threads((uuid, received_msg));
609 has_updates = true;
610 }
611 _ => {
612 self.send_message_to_all_threads((uuid, received_msg));
614 has_updates = true;
615 }
616 }
617 }
618
619 if !has_updates {
621 std::thread::sleep(std::time::Duration::from_millis(
622 self.app_context.config.frame_delay,
623 ));
624 }
625 }
626 }
627
628 pub fn spawn_thread<R: Runnable + 'static>(&mut self, mut runnable: R) -> Uuid {
629 let uuid = Uuid::new_v4();
630 let (s_tm_t_s, s_tm_t_r) = mpsc::channel::<(Uuid, Vec<FieldUpdate>)>();
631 let (s_t_tm_s, s_t_tm_r) = mpsc::channel::<(Uuid, Vec<FieldUpdate>)>();
632 let (m_tm_t_s, m_tm_t_r) = mpsc::channel::<(Uuid, Message)>();
633 let (m_t_tm_s, m_t_tm_r) = mpsc::channel::<(Uuid, Message)>();
634
635 runnable.set_uuid(uuid);
636 runnable.set_app_context_sender(s_t_tm_s);
637 runnable.set_message_sender(m_t_tm_s);
638 runnable.set_app_context_receiver(s_tm_t_r);
639 runnable.set_message_receiver(m_tm_t_r);
640
641 self.app_context_senders.insert(uuid, s_tm_t_s);
642 self.message_senders.insert(uuid, m_tm_t_s);
643 self.app_context_receivers.insert(uuid, s_t_tm_r);
644 self.message_receivers.insert(uuid, m_t_tm_r);
645
646 let runnable_class_name = std::any::type_name::<R>();
647 let thread_name = format!("{}_{}", runnable_class_name, uuid);
648
649 let handle = thread::Builder::new()
650 .name(thread_name)
651 .spawn(move || {
652 let mut continue_running = true;
653 while continue_running {
654 let result = runnable.run();
655 if let Err(e) = result {
656 error!("Runnable encountered an error: {}", e);
657 continue_running = false;
658 } else if let Ok(should_continue) = result {
659 continue_running = should_continue;
660 if !continue_running {
661 log::trace!("Stopping thread as directed by run method");
662 }
663 }
664 }
665 })
666 .unwrap();
667
668 self.threads.insert(uuid, handle);
669
670 log::trace!("Thread spawned: {}", uuid);
671
672 uuid
673 }
674
675 pub fn send_app_context_update_to_thread(&self, field_updates: Vec<FieldUpdate>, uuid: Uuid) {
676 if let Some(sender) = self.app_context_senders.get(&uuid) {
677 if let Err(e) = sender.send((uuid, field_updates)) {
678 error!("Failed to send data to thread: {}", e);
679 }
680 }
681 }
682
683 pub fn send_app_context_update_to_all_threads(&self, field_updates: (Uuid, Vec<FieldUpdate>)) {
684 for (&uuid, sender) in &self.app_context_senders {
685 if uuid != field_updates.0 {
686 if let Err(e) = sender.send(field_updates.clone()) {
687 error!("Failed to send update to thread: {}", e);
688 }
689 } else {
690 log::trace!("Skipping sending update to thread: {}", uuid);
691 }
692 }
693 }
694
695 pub fn send_message_to_thread(&self, msg: (Uuid, Message), uuid: Uuid) {
696 if let Some(sender) = self.message_senders.get(&uuid) {
697 log::info!("ThreadManager found message sender for thread: {}", uuid);
698 if let Err(e) = sender.send(msg) {
699 log::error!("Failed to send message to thread {}: {}", uuid, e);
700 } else {
701 log::info!(
702 "ThreadManager successfully sent message to thread: {}",
703 uuid
704 );
705 }
706 } else {
707 log::error!(
708 "ThreadManager could not find message sender for thread: {}",
709 uuid
710 );
711 }
712 }
713
714 pub fn send_message_to_all_threads(&self, msg: (Uuid, Message)) {
715 for (&uuid, sender) in &self.message_senders {
716 if uuid != msg.0 {
717 if let Err(e) = sender.send(msg.clone()) {
718 error!("Failed to send message to thread: {}", e);
719 }
720 }
721 }
722 }
723
724 pub fn join_threads(&mut self) {
725 for handle in self.threads.drain() {
726 if let Err(e) = handle.1.join() {
727 error!("Failed to join thread: {:?}", e);
728 }
729 }
730 }
731
732 pub fn get_hash<T: Hash>(&self, t: &T) -> u64 {
733 let mut hasher = DefaultHasher::new();
734 t.hash(&mut hasher);
735 hasher.finish()
736 }
737
738 pub fn remove_thread(&mut self, uuid: Uuid) {
739 if let Some(handle) = self.threads.remove(&uuid) {
740 if let Err(e) = handle.join() {
741 error!("Failed to join thread: {:?}", e);
742 }
743 }
744 let msg = (Uuid::new_v4(), Message::Exit);
745 self.send_message_to_thread(msg, uuid);
746 self.app_context_senders.remove(&uuid);
747 self.message_senders.remove(&uuid);
748 }
749}
750
751#[macro_export]
752macro_rules! create_runnable {
753 ($name:ident, $init_body:expr, $process_body:expr) => {
754 pub struct $name {
755 inner: RunnableImpl,
756 }
757
758 impl $name {
759 pub fn new(app_context: AppContext) -> Self {
760 $name {
761 inner: RunnableImpl::new(app_context),
762 }
763 }
764 }
765
766 impl Runnable for $name {
767 fn run(&mut self) -> Result<bool, Box<dyn std::error::Error>> {
768 {
770 let inner = &mut self.inner;
771 let app_context = inner.app_context.clone();
772 let messages = Vec::new();
773 let init_result = $init_body(inner, app_context, messages);
774 if !init_result {
775 return Ok(false);
776 }
777 }
778 self.inner._run(&mut |inner, app_context, messages| {
779 $process_body(inner, app_context, messages)
780 })
781 }
782
783 fn receive_updates(&mut self) -> (AppContext, Vec<Message>) {
784 self.inner.receive_updates()
785 }
786
787 fn process(&mut self, app_context: AppContext, messages: Vec<Message>) {
788 self.inner.process(app_context, messages)
789 }
790
791 fn update_app_context(&mut self, app_context: AppContext) {
792 self.inner.update_app_context(app_context)
793 }
794
795 fn set_uuid(&mut self, uuid: Uuid) {
796 self.inner.set_uuid(uuid)
797 }
798
799 fn get_uuid(&self) -> Uuid {
800 self.inner.get_uuid()
801 }
802
803 fn set_app_context_sender(
804 &mut self,
805 app_context_sender: mpsc::Sender<(Uuid, Vec<FieldUpdate>)>,
806 ) {
807 self.inner.set_app_context_sender(app_context_sender)
808 }
809
810 fn set_message_sender(&mut self, message_sender: mpsc::Sender<(Uuid, Message)>) {
811 self.inner.set_message_sender(message_sender)
812 }
813
814 fn set_app_context_receiver(
815 &mut self,
816 app_context_receiver: mpsc::Receiver<(Uuid, Vec<FieldUpdate>)>,
817 ) {
818 self.inner.set_app_context_receiver(app_context_receiver)
819 }
820
821 fn set_message_receiver(&mut self, message_receiver: mpsc::Receiver<(Uuid, Message)>) {
822 self.inner.set_message_receiver(message_receiver)
823 }
824
825 fn get_app_context(&self) -> &AppContext {
826 self.inner.get_app_context()
827 }
828
829 fn get_app_context_sender(&self) -> &Option<mpsc::Sender<(Uuid, Vec<FieldUpdate>)>> {
830 self.inner.get_app_context_sender()
831 }
832
833 fn get_message_sender(&self) -> &Option<mpsc::Sender<(Uuid, Message)>> {
834 self.inner.get_message_sender()
835 }
836
837 fn send_app_context_update(&self, old_app_context: AppContext) {
838 self.inner.send_app_context_update(old_app_context)
839 }
840
841 fn send_message(&self, msg: Message) {
842 self.inner.send_message(msg)
843 }
844 }
845 };
846}
847
848create_runnable!(
850 ChoiceExecutionRunnable,
851 |inner: &mut RunnableImpl, _app_context: AppContext, _messages: Vec<Message>| -> bool {
852 log::info!(
854 "ChoiceExecutionRunnable initialization: state={:?}",
855 inner.running_state
856 );
857 inner.running_state = RunnableState::Running;
858 log::info!("ChoiceExecutionRunnable set state to Running");
859 true
860 },
861 |inner: &mut RunnableImpl,
862 app_context: AppContext,
863 messages: Vec<Message>|
864 -> (bool, AppContext) {
865 let message_count = messages.len();
867 log::info!(
868 "ChoiceExecutionRunnable processing function called with {} messages",
869 message_count
870 );
871
872 let mut has_executed_choice = false;
873 for message in messages {
874 if let Message::ExecuteChoice(choice, muxbox_id, libs) = message {
875 log::info!(
876 "ChoiceExecutionRunnable executing choice: {} for muxbox: {}",
877 choice.id,
878 muxbox_id
879 );
880 has_executed_choice = true;
881
882 let choice_id = choice.id.clone();
884 let use_pty = choice.pty.unwrap_or(false);
885 let redirect_target = choice.redirect_output.clone();
886
887 let result = if let Some(script) = &choice.script {
888 log::info!(
889 "ChoiceExecutionRunnable running script for choice: {} (pty: {})",
890 choice_id,
891 use_pty
892 );
893
894 if use_pty {
895 let message_sender = inner
897 .message_sender
898 .clone()
899 .map(|sender| (sender, inner.uuid));
900 match run_script_with_pty_and_redirect(
901 libs,
902 script,
903 use_pty,
904 app_context.pty_manager.as_ref().map(|v| &**v),
905 Some(muxbox_id.clone()),
906 message_sender,
907 redirect_target,
908 ) {
909 Ok(output) => {
910 log::info!("ChoiceExecutionRunnable PTY script started successfully for choice: {}", choice_id);
911 Ok(output)
912 }
913 Err(e) => {
914 log::error!(
915 "ChoiceExecutionRunnable PTY script failed for choice: {}: {}",
916 choice_id,
917 e
918 );
919 Err(e.to_string())
920 }
921 }
922 } else {
923 match run_script(libs, script) {
925 Ok(output) => {
926 log::info!("ChoiceExecutionRunnable script completed successfully for choice: {}", choice_id);
927 Ok(output)
928 }
929 Err(e) => {
930 log::error!(
931 "ChoiceExecutionRunnable script failed for choice: {}: {}",
932 choice_id,
933 e
934 );
935 Err(e.to_string())
936 }
937 }
938 }
939 } else {
940 log::error!(
941 "ChoiceExecutionRunnable no script defined for choice: {}",
942 choice_id
943 );
944 Err("No script defined for choice".to_string())
945 };
946
947 log::info!(
948 "ChoiceExecutionRunnable sending completion message for choice: {}",
949 choice_id
950 );
951 inner.send_message(Message::ChoiceExecutionComplete(
953 choice_id, muxbox_id, result,
954 ));
955 }
956 }
957
958 let should_continue = !has_executed_choice; log::info!(
961 "ChoiceExecutionRunnable: messages={}, has_executed_choice={}, should_continue={}",
962 message_count,
963 has_executed_choice,
964 should_continue
965 );
966 (should_continue, app_context)
967 }
968);
969
970#[macro_export]
973macro_rules! create_runnable_with_dynamic_input {
974 ($name:ident, $vec_fn:expr, $init_body:expr, $process_body:expr) => {
975 pub struct $name {
976 inner: RunnableImpl,
977 vec_fn: Box<dyn Fn() -> Vec<String> + Send>,
978 }
979
980 impl $name {
981 pub fn new(
982 app_context: AppContext,
983 vec_fn: Box<dyn Fn() -> Vec<String> + Send>,
984 ) -> Self {
985 $name {
986 inner: RunnableImpl::new(app_context),
987 vec_fn,
988 }
989 }
990 }
991
992 impl Runnable for $name {
993 fn run(&mut self) -> Result<bool, Box<dyn std::error::Error>> {
994 {
996 let inner = &mut self.inner;
997 let app_context = inner.app_context.clone();
998 let messages = Vec::new();
999 let vec = (self.vec_fn)();
1000 let init_result = $init_body(inner, app_context, messages, vec);
1001 if !init_result {
1002 return Ok(false);
1003 }
1004 }
1005 self.inner._run(&mut |inner, app_context, messages| {
1006 let vec = (self.vec_fn)();
1007 $process_body(inner, app_context, messages, vec)
1008 })
1009 }
1010
1011 fn receive_updates(&mut self) -> (AppContext, Vec<Message>) {
1012 self.inner.receive_updates()
1013 }
1014
1015 fn process(&mut self, app_context: AppContext, messages: Vec<Message>) {
1016 self.inner.process(app_context.clone(), messages.clone());
1017 }
1018
1019 fn update_app_context(&mut self, app_context: AppContext) {
1020 self.inner.update_app_context(app_context)
1021 }
1022
1023 fn set_uuid(&mut self, uuid: Uuid) {
1024 self.inner.set_uuid(uuid)
1025 }
1026
1027 fn get_uuid(&self) -> Uuid {
1028 self.inner.get_uuid()
1029 }
1030
1031 fn set_app_context_sender(
1032 &mut self,
1033 app_context_sender: mpsc::Sender<(Uuid, Vec<FieldUpdate>)>,
1034 ) {
1035 self.inner.set_app_context_sender(app_context_sender)
1036 }
1037
1038 fn set_message_sender(&mut self, message_sender: mpsc::Sender<(Uuid, Message)>) {
1039 self.inner.set_message_sender(message_sender)
1040 }
1041
1042 fn set_app_context_receiver(
1043 &mut self,
1044 app_context_receiver: mpsc::Receiver<(Uuid, Vec<FieldUpdate>)>,
1045 ) {
1046 self.inner.set_app_context_receiver(app_context_receiver)
1047 }
1048
1049 fn set_message_receiver(&mut self, message_receiver: mpsc::Receiver<(Uuid, Message)>) {
1050 self.inner.set_message_receiver(message_receiver)
1051 }
1052
1053 fn get_app_context(&self) -> &AppContext {
1054 self.inner.get_app_context()
1055 }
1056
1057 fn get_app_context_sender(&self) -> &Option<mpsc::Sender<(Uuid, Vec<FieldUpdate>)>> {
1058 self.inner.get_app_context_sender()
1059 }
1060
1061 fn get_message_sender(&self) -> &Option<mpsc::Sender<(Uuid, Message)>> {
1062 self.inner.get_message_sender()
1063 }
1064
1065 fn send_app_context_update(&self, old_app_context: AppContext) {
1066 self.inner.send_app_context_update(old_app_context)
1067 }
1068
1069 fn send_message(&self, msg: Message) {
1070 self.inner.send_message(msg)
1071 }
1072 }
1073 };
1074}
1075
1076pub fn run_script_in_thread(
1077 app_context: AppContext,
1078 manager: &mut ThreadManager,
1079 muxbox_id: String,
1080 choice_id: String,
1081) -> Uuid {
1082 let vec_fn = move || vec![muxbox_id.clone(), choice_id.clone()];
1083
1084 create_runnable_with_dynamic_input!(
1085 ChoiceScriptRunner,
1086 Box::new(vec_fn),
1087 |_inner: &mut RunnableImpl,
1088 _app_context: AppContext,
1089 _messages: Vec<Message>,
1090 _vec: Vec<String>|
1091 -> bool { true },
1092 |inner: &mut RunnableImpl,
1093 app_context: AppContext,
1094 _messages: Vec<Message>,
1095 vec: Vec<String>|
1096 -> (bool, AppContext) {
1097 let mut app_context_unwrapped = app_context.clone();
1098 let app_graph = app_context_unwrapped.app.generate_graph();
1099 let libs = app_context_unwrapped.app.libs.clone();
1100 let muxbox = app_context_unwrapped
1101 .app
1102 .get_muxbox_by_id_mut(&vec[0])
1103 .unwrap();
1104 let choice = muxbox
1105 .choices
1106 .as_mut()
1107 .unwrap()
1108 .iter_mut()
1109 .find(|c| c.id == vec[1])
1110 .unwrap();
1111 let use_pty = crate::utils::should_use_pty_for_choice(choice);
1113 let pty_manager = app_context_unwrapped.pty_manager.as_ref();
1114 let message_sender = Some((
1115 inner.get_message_sender().as_ref().unwrap().clone(),
1116 inner.get_uuid(),
1117 ));
1118
1119 match crate::utils::run_script_with_pty(
1120 libs,
1121 choice.script.clone().unwrap().as_ref(),
1122 use_pty,
1123 pty_manager.map(|arc| arc.as_ref()),
1124 Some(choice.id.clone()),
1125 message_sender,
1126 ) {
1127 Ok(output) => {
1128 inner.send_message(Message::MuxBoxOutputUpdate(choice.id.clone(), true, output))
1129 }
1130 Err(e) => inner.send_message(Message::MuxBoxOutputUpdate(
1131 choice.id.clone(),
1132 false,
1133 e.to_string(),
1134 )),
1135 }
1136 std::thread::sleep(std::time::Duration::from_millis(
1137 muxbox.calc_refresh_interval(&app_context, &app_graph),
1138 ));
1139 (false, app_context_unwrapped)
1140 }
1141 );
1142
1143 let choice_refresh_loop = ChoiceScriptRunner::new(app_context.clone(), Box::new(vec_fn));
1144 manager.spawn_thread(choice_refresh_loop)
1145}
1146
1147#[cfg(test)]
1424mod tests {
1425 use super::*;
1426 use crate::model::app::App;
1427 use crate::model::common::{Config, EntityType, FieldUpdate};
1428 use crate::model::layout::Layout;
1429 use crate::model::muxbox::MuxBox;
1430 use serde_json::Value;
1431 use std::sync::mpsc;
1432
1433 fn create_test_app_context() -> AppContext {
1435 let mut app = App::new();
1436 let layout = create_test_layout("test_layout");
1437 app.layouts.push(layout);
1438 let config = Config::default();
1439 AppContext::new(app, config)
1440 }
1441
1442 fn create_test_muxbox(id: &str) -> MuxBox {
1444 MuxBox {
1445 id: id.to_string(),
1446 position: crate::model::common::InputBounds {
1447 x1: "0%".to_string(),
1448 y1: "0%".to_string(),
1449 x2: "100%".to_string(),
1450 y2: "100%".to_string(),
1451 },
1452 ..Default::default()
1453 }
1454 }
1455
1456 fn create_test_layout(id: &str) -> Layout {
1458 Layout {
1459 id: id.to_string(),
1460 ..Default::default()
1461 }
1462 }
1463
1464 fn create_test_field_update(
1466 entity_type: EntityType,
1467 entity_id: &str,
1468 field_name: &str,
1469 value: Value,
1470 ) -> FieldUpdate {
1471 FieldUpdate {
1472 entity_type,
1473 entity_id: Some(entity_id.to_string()),
1474 field_name: field_name.to_string(),
1475 new_value: value,
1476 }
1477 }
1478
1479 #[test]
1482 fn test_message_hash() {
1483 let mut hasher1 = DefaultHasher::new();
1484 let mut hasher2 = DefaultHasher::new();
1485
1486 let msg1 = Message::Exit;
1487 let msg2 = Message::Exit;
1488 msg1.hash(&mut hasher1);
1489 msg2.hash(&mut hasher2);
1490 assert_eq!(hasher1.finish(), hasher2.finish());
1491
1492 let msg3 = Message::RedrawMuxBox("muxbox1".to_string());
1493 let msg4 = Message::RedrawMuxBox("muxbox1".to_string());
1494 let mut hasher3 = DefaultHasher::new();
1495 let mut hasher4 = DefaultHasher::new();
1496 msg3.hash(&mut hasher3);
1497 msg4.hash(&mut hasher4);
1498 assert_eq!(hasher3.finish(), hasher4.finish());
1499 }
1500
1501 #[test]
1504 fn test_message_equality() {
1505 assert_eq!(Message::Exit, Message::Exit);
1506 assert_eq!(Message::Terminate, Message::Terminate);
1507 assert_eq!(
1508 Message::RedrawMuxBox("muxbox1".to_string()),
1509 Message::RedrawMuxBox("muxbox1".to_string())
1510 );
1511 assert_ne!(
1512 Message::RedrawMuxBox("muxbox1".to_string()),
1513 Message::RedrawMuxBox("muxbox2".to_string())
1514 );
1515 assert_ne!(Message::Exit, Message::Terminate);
1516 }
1517
1518 #[test]
1521 fn test_message_hash_different_types() {
1522 let msg1 = Message::KeyPress("a".to_string());
1523 let msg2 = Message::KeyPress("a".to_string());
1524 let msg3 = Message::KeyPress("b".to_string());
1525
1526 let mut hasher1 = DefaultHasher::new();
1527 let mut hasher2 = DefaultHasher::new();
1528 let mut hasher3 = DefaultHasher::new();
1529
1530 msg1.hash(&mut hasher1);
1531 msg2.hash(&mut hasher2);
1532 msg3.hash(&mut hasher3);
1533
1534 assert_eq!(hasher1.finish(), hasher2.finish());
1535 assert_ne!(hasher1.finish(), hasher3.finish());
1536 }
1537
1538 #[test]
1541 fn test_runnable_state() {
1542 let state1 = RunnableState::Created;
1543 let state2 = state1.clone();
1544 assert_eq!(state1, state2);
1545
1546 let state3 = RunnableState::Running;
1547 assert_ne!(state1, state3);
1548
1549 let state4 = RunnableState::Paused;
1550 let state5 = RunnableState::Terminated;
1551 assert_ne!(state4, state5);
1552 }
1553
1554 #[test]
1557 fn test_runnable_impl_new() {
1558 let app_context = create_test_app_context();
1559 let runnable = RunnableImpl::new(app_context.clone());
1560
1561 assert_eq!(runnable.get_app_context(), &app_context);
1562 assert_eq!(runnable.running_state, RunnableState::Created);
1563 assert!(runnable.app_context_sender.is_none());
1564 assert!(runnable.message_sender.is_none());
1565 assert!(runnable.app_context_receiver.is_none());
1566 assert!(runnable.message_receiver.is_none());
1567 }
1568
1569 #[test]
1572 fn test_runnable_impl_uuid() {
1573 let app_context = create_test_app_context();
1574 let mut runnable = RunnableImpl::new(app_context);
1575
1576 let original_uuid = runnable.get_uuid();
1577 let new_uuid = Uuid::new_v4();
1578 runnable.set_uuid(new_uuid);
1579
1580 assert_eq!(runnable.get_uuid(), new_uuid);
1581 assert_ne!(runnable.get_uuid(), original_uuid);
1582 }
1583
1584 #[test]
1587 fn test_runnable_impl_channels() {
1588 let app_context = create_test_app_context();
1589 let mut runnable = RunnableImpl::new(app_context);
1590
1591 let (app_context_sender, app_context_receiver) = mpsc::channel();
1592 let (message_sender, message_receiver) = mpsc::channel();
1593
1594 runnable.set_app_context_sender(app_context_sender);
1595 runnable.set_message_sender(message_sender);
1596 runnable.set_app_context_receiver(app_context_receiver);
1597 runnable.set_message_receiver(message_receiver);
1598
1599 assert!(runnable.get_app_context_sender().is_some());
1600 assert!(runnable.get_message_sender().is_some());
1601 assert!(runnable.app_context_receiver.is_some());
1602 assert!(runnable.message_receiver.is_some());
1603 }
1604
1605 #[test]
1608 fn test_runnable_impl_update_app_context() {
1609 let app_context = create_test_app_context();
1610 let mut runnable = RunnableImpl::new(app_context);
1611
1612 let mut new_app_context = create_test_app_context();
1613 new_app_context.config.frame_delay = 100;
1614
1615 runnable.update_app_context(new_app_context.clone());
1616 assert_eq!(runnable.get_app_context(), &new_app_context);
1617 }
1618
1619 #[test]
1622 fn test_runnable_impl_receive_updates() {
1623 let app_context = create_test_app_context();
1624 let mut runnable = RunnableImpl::new(app_context);
1625
1626 let (app_context_sender, app_context_receiver) = mpsc::channel();
1627 let (message_sender, message_receiver) = mpsc::channel();
1628
1629 runnable.set_app_context_receiver(app_context_receiver);
1630 runnable.set_message_receiver(message_receiver);
1631
1632 let field_update = create_test_field_update(
1634 EntityType::App,
1635 "test",
1636 "field",
1637 Value::String("value".to_string()),
1638 );
1639 app_context_sender
1640 .send((Uuid::new_v4(), vec![field_update]))
1641 .unwrap();
1642 message_sender
1643 .send((Uuid::new_v4(), Message::RedrawApp))
1644 .unwrap();
1645
1646 let (updated_app_context, messages) = runnable.receive_updates();
1647 assert_eq!(messages.len(), 1);
1648 assert_eq!(messages[0], Message::RedrawApp);
1649 }
1650
1651 #[test]
1654 fn test_runnable_impl_process_messages() {
1655 let app_context = create_test_app_context();
1656 let mut runnable = RunnableImpl::new(app_context.clone());
1657
1658 let messages = vec![Message::Start, Message::Pause, Message::Terminate];
1659
1660 runnable.process(app_context.clone(), vec![Message::Start]);
1661 assert_eq!(runnable.running_state, RunnableState::Running);
1662
1663 runnable.process(app_context.clone(), vec![Message::Pause]);
1664 assert_eq!(runnable.running_state, RunnableState::Paused);
1665
1666 runnable.process(app_context.clone(), vec![Message::Terminate]);
1667 assert_eq!(runnable.running_state, RunnableState::Terminated);
1668 }
1669
1670 #[test]
1673 fn test_runnable_impl_send_message() {
1674 let app_context = create_test_app_context();
1675 let mut runnable = RunnableImpl::new(app_context);
1676
1677 let (message_sender, message_receiver) = mpsc::channel();
1678 runnable.set_message_sender(message_sender);
1679
1680 runnable.send_message(Message::RedrawApp);
1681
1682 let (uuid, received_message) = message_receiver.recv().unwrap();
1683 assert_eq!(received_message, Message::RedrawApp);
1684 assert_eq!(uuid, runnable.get_uuid());
1685 }
1686
1687 #[test]
1690 fn test_thread_manager_new() {
1691 let app_context = create_test_app_context();
1692 let manager = ThreadManager::new(app_context.clone());
1693
1694 assert_eq!(manager.app_context, app_context);
1695 assert!(manager.threads.is_empty());
1696 assert!(manager.app_context_senders.is_empty());
1697 assert!(manager.message_senders.is_empty());
1698 assert!(manager.app_context_receivers.is_empty());
1699 assert!(manager.message_receivers.is_empty());
1700 }
1701
1702 #[test]
1705 fn test_thread_manager_spawn_thread() {
1706 let app_context = create_test_app_context();
1707 let mut manager = ThreadManager::new(app_context.clone());
1708 let runnable = RunnableImpl::new(app_context);
1709
1710 let uuid = manager.spawn_thread(runnable);
1711
1712 assert!(manager.threads.contains_key(&uuid));
1713 assert!(manager.app_context_senders.contains_key(&uuid));
1714 assert!(manager.message_senders.contains_key(&uuid));
1715 assert!(manager.app_context_receivers.contains_key(&uuid));
1716 assert!(manager.message_receivers.contains_key(&uuid));
1717
1718 manager.stop();
1720 manager.join_threads();
1721 }
1722
1723 #[test]
1726 fn test_thread_manager_remove_thread() {
1727 let app_context = create_test_app_context();
1728 let mut manager = ThreadManager::new(app_context.clone());
1729 let runnable = RunnableImpl::new(app_context);
1730
1731 let uuid = manager.spawn_thread(runnable);
1732 assert!(manager.threads.contains_key(&uuid));
1733
1734 manager.remove_thread(uuid);
1735 assert!(!manager.threads.contains_key(&uuid));
1736 assert!(!manager.app_context_senders.contains_key(&uuid));
1737 assert!(!manager.message_senders.contains_key(&uuid));
1738 }
1739
1740 #[test]
1743 fn test_thread_manager_send_message_to_thread() {
1744 let app_context = create_test_app_context();
1745 let mut manager = ThreadManager::new(app_context.clone());
1746 let runnable = RunnableImpl::new(app_context);
1747
1748 let uuid = manager.spawn_thread(runnable);
1749 let message = (Uuid::new_v4(), Message::RedrawApp);
1750
1751 manager.send_message_to_thread(message, uuid);
1752
1753 manager.stop();
1755 manager.join_threads();
1756 }
1757
1758 #[test]
1761 fn test_thread_manager_send_message_to_all_threads() {
1762 let app_context = create_test_app_context();
1763 let mut manager = ThreadManager::new(app_context.clone());
1764 let runnable1 = RunnableImpl::new(app_context.clone());
1765 let runnable2 = RunnableImpl::new(app_context.clone());
1766
1767 let uuid1 = manager.spawn_thread(runnable1);
1768 let uuid2 = manager.spawn_thread(runnable2);
1769
1770 let message = (Uuid::new_v4(), Message::RedrawApp);
1771 manager.send_message_to_all_threads(message);
1772
1773 manager.stop();
1775 manager.join_threads();
1776 }
1777
1778 #[test]
1781 fn test_thread_manager_send_app_context_update_to_thread() {
1782 let app_context = create_test_app_context();
1783 let mut manager = ThreadManager::new(app_context.clone());
1784 let runnable = RunnableImpl::new(app_context);
1785
1786 let uuid = manager.spawn_thread(runnable);
1787 let field_update = create_test_field_update(
1788 EntityType::App,
1789 "test",
1790 "field",
1791 Value::String("value".to_string()),
1792 );
1793
1794 manager.send_app_context_update_to_thread(vec![field_update], uuid);
1795
1796 manager.stop();
1798 manager.join_threads();
1799 }
1800
1801 #[test]
1804 fn test_thread_manager_send_app_context_update_to_all_threads() {
1805 let app_context = create_test_app_context();
1806 let mut manager = ThreadManager::new(app_context.clone());
1807 let runnable1 = RunnableImpl::new(app_context.clone());
1808 let runnable2 = RunnableImpl::new(app_context.clone());
1809
1810 let uuid1 = manager.spawn_thread(runnable1);
1811 let uuid2 = manager.spawn_thread(runnable2);
1812
1813 let field_update = create_test_field_update(
1814 EntityType::App,
1815 "test",
1816 "field",
1817 Value::String("value".to_string()),
1818 );
1819 let sender_uuid = Uuid::new_v4();
1820
1821 manager.send_app_context_update_to_all_threads((sender_uuid, vec![field_update]));
1822
1823 manager.stop();
1825 manager.join_threads();
1826 }
1827
1828 #[test]
1831 fn test_thread_manager_stop() {
1832 let app_context = create_test_app_context();
1833 let mut manager = ThreadManager::new(app_context.clone());
1834 let runnable = RunnableImpl::new(app_context);
1835
1836 let uuid = manager.spawn_thread(runnable);
1837 assert!(manager.threads.contains_key(&uuid));
1838
1839 manager.stop();
1840 manager.join_threads();
1841
1842 assert!(manager.threads.is_empty());
1843 }
1844
1845 #[test]
1848 fn test_thread_manager_pause() {
1849 let app_context = create_test_app_context();
1850 let mut manager = ThreadManager::new(app_context.clone());
1851 let runnable = RunnableImpl::new(app_context);
1852
1853 let uuid = manager.spawn_thread(runnable);
1854 manager.pause();
1855
1856 manager.stop();
1858 manager.join_threads();
1859 }
1860
1861 #[test]
1864 fn test_thread_manager_get_hash() {
1865 let app_context = create_test_app_context();
1866 let manager = ThreadManager::new(app_context);
1867
1868 let test_string = "test";
1869 let hash1 = manager.get_hash(&test_string);
1870 let hash2 = manager.get_hash(&test_string);
1871 assert_eq!(hash1, hash2);
1872
1873 let test_string2 = "different";
1874 let hash3 = manager.get_hash(&test_string2);
1875 assert_ne!(hash1, hash3);
1876 }
1877
1878 #[test]
1881 fn test_thread_manager_join_threads() {
1882 let app_context = create_test_app_context();
1883 let mut manager = ThreadManager::new(app_context.clone());
1884 let runnable = RunnableImpl::new(app_context);
1885
1886 let uuid = manager.spawn_thread(runnable);
1887 assert!(manager.threads.contains_key(&uuid));
1888
1889 manager.stop();
1890 manager.join_threads();
1891
1892 assert!(manager.threads.is_empty());
1893 }
1894
1895 #[test]
1898 fn test_run_script_in_thread() {
1899 let app_context = create_test_app_context();
1900 let mut manager = ThreadManager::new(app_context.clone());
1901
1902 let muxbox_id = "test_muxbox".to_string();
1903 let choice_id = "test_choice".to_string();
1904
1905 let uuid = run_script_in_thread(app_context, &mut manager, muxbox_id, choice_id);
1906
1907 assert!(manager.threads.contains_key(&uuid));
1908 assert!(manager.app_context_senders.contains_key(&uuid));
1909 assert!(manager.message_senders.contains_key(&uuid));
1910
1911 manager.stop();
1913 manager.join_threads();
1914 }
1915
1916 #[test]
1919 fn test_message_muxbox_output_update() {
1920 let muxbox_id = "test_muxbox".to_string();
1921 let success = true;
1922 let output = "test output".to_string();
1923
1924 let message = Message::MuxBoxOutputUpdate(muxbox_id.clone(), success, output.clone());
1925
1926 match message {
1927 Message::MuxBoxOutputUpdate(id, success_flag, content) => {
1928 assert_eq!(id, muxbox_id);
1929 assert_eq!(success_flag, success);
1930 assert_eq!(content, output);
1931 }
1932 _ => panic!("Expected MuxBoxOutputUpdate message"),
1933 }
1934 }
1935
1936 #[test]
1939 fn test_message_muxbox_script_update() {
1940 let muxbox_id = "test_muxbox".to_string();
1941 let script = vec!["echo 'test'".to_string(), "ls".to_string()];
1942
1943 let message = Message::MuxBoxScriptUpdate(muxbox_id.clone(), script.clone());
1944
1945 match message {
1946 Message::MuxBoxScriptUpdate(id, script_content) => {
1947 assert_eq!(id, muxbox_id);
1948 assert_eq!(script_content, script);
1949 }
1950 _ => panic!("Expected MuxBoxScriptUpdate message"),
1951 }
1952 }
1953
1954 #[test]
1957 fn test_message_replace_muxbox() {
1958 let muxbox_id = "test_muxbox".to_string();
1959 let muxbox = create_test_muxbox("new_muxbox");
1960
1961 let message = Message::ReplaceMuxBox(muxbox_id.clone(), muxbox.clone());
1962
1963 match message {
1964 Message::ReplaceMuxBox(id, new_muxbox) => {
1965 assert_eq!(id, muxbox_id);
1966 assert_eq!(new_muxbox.id, muxbox.id);
1967 }
1968 _ => panic!("Expected ReplaceMuxBox message"),
1969 }
1970 }
1971
1972 #[test]
1975 fn test_message_switch_active_layout() {
1976 let layout_id = "test_layout".to_string();
1977 let message = Message::SwitchActiveLayout(layout_id.clone());
1978
1979 match message {
1980 Message::SwitchActiveLayout(id) => {
1981 assert_eq!(id, layout_id);
1982 }
1983 _ => panic!("Expected SwitchActiveLayout message"),
1984 }
1985 }
1986
1987 #[test]
1990 fn test_message_key_press() {
1991 let key = "ctrl+c".to_string();
1992 let message = Message::KeyPress(key.clone());
1993
1994 match message {
1995 Message::KeyPress(pressed_key) => {
1996 assert_eq!(pressed_key, key);
1997 }
1998 _ => panic!("Expected KeyPress message"),
1999 }
2000 }
2001
2002 #[test]
2005 fn test_message_external_message() {
2006 let external_msg = "external command".to_string();
2007 let message = Message::ExternalMessage(external_msg.clone());
2008
2009 match message {
2010 Message::ExternalMessage(msg) => {
2011 assert_eq!(msg, external_msg);
2012 }
2013 _ => panic!("Expected ExternalMessage message"),
2014 }
2015 }
2016
2017 #[test]
2020 fn test_message_add_box() {
2021 let box_id = "test_box".to_string();
2022 let muxbox = create_test_muxbox("new_box");
2023
2024 let message = Message::AddBox(box_id.clone(), muxbox.clone());
2025
2026 match message {
2027 Message::AddBox(id, new_muxbox) => {
2028 assert_eq!(id, box_id);
2029 assert_eq!(new_muxbox.id, muxbox.id);
2030 }
2031 _ => panic!("Expected AddBox message"),
2032 }
2033 }
2034
2035 #[test]
2038 fn test_message_remove_box() {
2039 let box_id = "test_box".to_string();
2040 let message = Message::RemoveBox(box_id.clone());
2041
2042 match message {
2043 Message::RemoveBox(id) => {
2044 assert_eq!(id, box_id);
2045 }
2046 _ => panic!("Expected RemoveBox message"),
2047 }
2048 }
2049
2050 #[test]
2053 fn test_simple_messages() {
2054 let exit_msg = Message::Exit;
2055 let terminate_msg = Message::Terminate;
2056 let pause_msg = Message::Pause;
2057 let start_msg = Message::Start;
2058 let resize_msg = Message::Resize;
2059 let redraw_app_msg = Message::RedrawApp;
2060
2061 assert_eq!(exit_msg, Message::Exit);
2063 assert_eq!(terminate_msg, Message::Terminate);
2064 assert_eq!(pause_msg, Message::Pause);
2065 assert_eq!(start_msg, Message::Start);
2066 assert_eq!(resize_msg, Message::Resize);
2067 assert_eq!(redraw_app_msg, Message::RedrawApp);
2068
2069 assert_ne!(exit_msg, terminate_msg);
2071 assert_ne!(pause_msg, start_msg);
2072 assert_ne!(resize_msg, redraw_app_msg);
2073 }
2074
2075 #[test]
2078 fn test_scroll_messages() {
2079 let scroll_down = Message::ScrollMuxBoxDown();
2080 let scroll_up = Message::ScrollMuxBoxUp();
2081 let scroll_left = Message::ScrollMuxBoxLeft();
2082 let scroll_right = Message::ScrollMuxBoxRight();
2083 let scroll_page_up = Message::ScrollMuxBoxPageUp();
2084 let scroll_page_down = Message::ScrollMuxBoxPageDown();
2085
2086 assert_eq!(scroll_down, Message::ScrollMuxBoxDown());
2087 assert_eq!(scroll_up, Message::ScrollMuxBoxUp());
2088 assert_eq!(scroll_left, Message::ScrollMuxBoxLeft());
2089 assert_eq!(scroll_right, Message::ScrollMuxBoxRight());
2090 assert_eq!(scroll_page_up, Message::ScrollMuxBoxPageUp());
2091 assert_eq!(scroll_page_down, Message::ScrollMuxBoxPageDown());
2092
2093 assert_ne!(scroll_down, scroll_up);
2094 assert_ne!(scroll_left, scroll_right);
2095 }
2096
2097 #[test]
2100 fn test_navigation_messages() {
2101 let next_muxbox = Message::NextMuxBox();
2102 let previous_muxbox = Message::PreviousMuxBox();
2103
2104 assert_eq!(next_muxbox, Message::NextMuxBox());
2105 assert_eq!(previous_muxbox, Message::PreviousMuxBox());
2106 assert_ne!(next_muxbox, previous_muxbox);
2107 }
2108
2109 #[test]
2112 fn test_box_refresh_messages() {
2113 let box_id = "test_box".to_string();
2114 let start_refresh = Message::StartBoxRefresh(box_id.clone());
2115 let stop_refresh = Message::StopBoxRefresh(box_id.clone());
2116 let event_refresh = Message::MuxBoxEventRefresh(box_id.clone());
2117
2118 match start_refresh {
2119 Message::StartBoxRefresh(id) => assert_eq!(id, box_id),
2120 _ => panic!("Expected StartBoxRefresh"),
2121 }
2122
2123 match stop_refresh {
2124 Message::StopBoxRefresh(id) => assert_eq!(id, box_id),
2125 _ => panic!("Expected StopBoxRefresh"),
2126 }
2127
2128 match event_refresh {
2129 Message::MuxBoxEventRefresh(id) => assert_eq!(id, box_id),
2130 _ => panic!("Expected MuxBoxEventRefresh"),
2131 }
2132 }
2133
2134 #[test]
2137 fn test_redraw_muxbox_message() {
2138 let muxbox_id = "test_muxbox".to_string();
2139 let redraw_msg = Message::RedrawMuxBox(muxbox_id.clone());
2140
2141 match redraw_msg {
2142 Message::RedrawMuxBox(id) => assert_eq!(id, muxbox_id),
2143 _ => panic!("Expected RedrawMuxBox message"),
2144 }
2145 }
2146}