Clippy is now added to the `check` in the pipeline Co-authored-by: Ian Letourneau <letourneau.ian@gmail.com> Reviewed-on: https://git.nationtech.io/NationTech/harmony/pulls/96
		
			
				
	
	
		
			159 lines
		
	
	
		
			4.8 KiB
		
	
	
	
		
			Rust
		
	
	
	
	
	
			
		
		
	
	
			159 lines
		
	
	
		
			4.8 KiB
		
	
	
	
		
			Rust
		
	
	
	
	
	
| use std::sync::{Arc, RwLock};
 | |
| 
 | |
| use crate::HarmonyTuiEvent;
 | |
| use crossterm::event::{Event, KeyCode, KeyEventKind};
 | |
| use harmony::{score::Score, topology::Topology};
 | |
| use log::{info, warn};
 | |
| use ratatui::{
 | |
|     Frame,
 | |
|     layout::Rect,
 | |
|     style::{Style, Stylize},
 | |
|     widgets::{List, ListItem, ListState, StatefulWidget, Widget},
 | |
| };
 | |
| use tokio::sync::mpsc;
 | |
| 
 | |
| #[derive(Debug)]
 | |
| enum ExecutionState {
 | |
|     Initiated,
 | |
|     Running,
 | |
|     Canceled,
 | |
| }
 | |
| 
 | |
| struct Execution<T: Topology> {
 | |
|     state: ExecutionState,
 | |
|     score: Box<dyn Score<T>>,
 | |
| }
 | |
| 
 | |
| impl<T: Topology> std::fmt::Display for Execution<T> {
 | |
|     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
 | |
|         f.write_fmt(format_args!(
 | |
|             "Execution of {} status {:?}",
 | |
|             self.score.name(),
 | |
|             self.state
 | |
|         ))
 | |
|     }
 | |
| }
 | |
| 
 | |
| pub(crate) struct ScoreListWidget<T: Topology> {
 | |
|     list_state: Arc<RwLock<ListState>>,
 | |
|     scores: Vec<Box<dyn Score<T>>>,
 | |
|     execution: Option<Execution<T>>,
 | |
|     execution_history: Vec<Execution<T>>,
 | |
|     sender: mpsc::Sender<HarmonyTuiEvent<T>>,
 | |
| }
 | |
| 
 | |
| impl<T: Topology> ScoreListWidget<T> {
 | |
|     pub(crate) fn new(
 | |
|         scores: Vec<Box<dyn Score<T>>>,
 | |
|         sender: mpsc::Sender<HarmonyTuiEvent<T>>,
 | |
|     ) -> Self {
 | |
|         let mut list_state = ListState::default();
 | |
|         list_state.select_first();
 | |
|         let list_state = Arc::new(RwLock::new(list_state));
 | |
|         Self {
 | |
|             scores,
 | |
|             list_state,
 | |
|             execution: None,
 | |
|             execution_history: vec![],
 | |
|             sender,
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     pub(crate) fn launch_execution(&mut self) {
 | |
|         if let Some(score) = self.get_selected_score() {
 | |
|             self.execution = Some(Execution {
 | |
|                 state: ExecutionState::Initiated,
 | |
|                 score: score.clone_box(),
 | |
|             });
 | |
|             info!("{}\n\nConfirm Execution (Press y/n)", score.name());
 | |
|             info!("{}", score.print_score_details());
 | |
|         } else {
 | |
|             warn!("No Score selected, nothing to launch");
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     pub(crate) fn get_selected_score(&self) -> Option<Box<dyn Score<T>>> {
 | |
|         let list_read = self.list_state.read().unwrap();
 | |
|         if let Some(index) = list_read.selected() {
 | |
|             self.scores.get(index).map(|s| s.clone_box())
 | |
|         } else {
 | |
|             None
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     pub(crate) fn scroll_down(&self) {
 | |
|         self.list_state.write().unwrap().scroll_down_by(1);
 | |
|     }
 | |
| 
 | |
|     pub(crate) fn scroll_up(&self) {
 | |
|         self.list_state.write().unwrap().scroll_up_by(1);
 | |
|     }
 | |
| 
 | |
|     pub(crate) fn render(&self, area: Rect, frame: &mut Frame) {
 | |
|         frame.render_widget(self, area);
 | |
|     }
 | |
| 
 | |
|     fn clear_execution(&mut self) {
 | |
|         match self.execution.take() {
 | |
|             Some(execution) => {
 | |
|                 self.execution_history.push(execution);
 | |
|             }
 | |
|             None => warn!("Should never clear execution when no execution exists"),
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     pub(crate) async fn confirm(&mut self, confirm: bool) {
 | |
|         if let Some(execution) = &mut self.execution {
 | |
|             match confirm {
 | |
|                 true => {
 | |
|                     execution.state = ExecutionState::Running;
 | |
|                     info!("Launch execution {execution}");
 | |
|                     self.sender
 | |
|                         .send(HarmonyTuiEvent::LaunchScore(execution.score.clone_box()))
 | |
|                         .await
 | |
|                         .expect("Should be able to send message");
 | |
|                 }
 | |
|                 false => {
 | |
|                     execution.state = ExecutionState::Canceled;
 | |
|                     info!("Execution cancelled");
 | |
|                     self.clear_execution();
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     pub(crate) async fn handle_event(&mut self, event: &Event) {
 | |
|         if let Event::Key(key) = event {
 | |
|             if key.kind == KeyEventKind::Press {
 | |
|                 match key.code {
 | |
|                     KeyCode::Char('j') | KeyCode::Down => self.scroll_down(),
 | |
|                     KeyCode::Char('k') | KeyCode::Up => self.scroll_up(),
 | |
|                     KeyCode::Enter => self.launch_execution(),
 | |
|                     KeyCode::Char('y') => self.confirm(true).await,
 | |
|                     KeyCode::Char('n') => self.confirm(false).await,
 | |
|                     _ => {}
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| }
 | |
| 
 | |
| impl<T: Topology> Widget for &ScoreListWidget<T> {
 | |
|     fn render(self, area: ratatui::prelude::Rect, buf: &mut ratatui::prelude::Buffer)
 | |
|     where
 | |
|         Self: Sized,
 | |
|     {
 | |
|         let mut list_state = self.list_state.write().unwrap();
 | |
|         let scores_items: Vec<ListItem<'_>> = self
 | |
|             .scores
 | |
|             .iter()
 | |
|             .map(|score| ListItem::new(score.name()))
 | |
|             .collect();
 | |
|         let list = List::new(scores_items)
 | |
|             .highlight_style(Style::new().bold().italic())
 | |
|             .highlight_symbol("🠊 ");
 | |
| 
 | |
|         StatefulWidget::render(list, area, buf, &mut list_state)
 | |
|     }
 | |
| }
 |