use std::sync::Arc; use color_eyre::eyre::Context; use color_eyre::Help; use color_eyre::Report; use color_eyre::Result; use druid::{ExtEventSink, SingleUse, Target}; use tokio::runtime::Runtime; use tokio::sync::mpsc::UnboundedReceiver; use tokio::sync::RwLock; use crate::controller::app::*; use crate::controller::game::*; use crate::state::AsyncAction; use crate::state::ACTION_FINISH_CHECK_UPDATE; use crate::state::ACTION_FINISH_LOAD_INITIAL; use crate::state::ACTION_FINISH_SAVE_SETTINGS; use crate::state::ACTION_SHOW_ERROR_DIALOG; use crate::state::{ ACTION_FINISH_ADD_MOD, ACTION_FINISH_DELETE_SELECTED_MOD, ACTION_FINISH_DEPLOY, ACTION_FINISH_RESET_DEPLOYMENT, ACTION_LOG, }; async fn send_error(sink: Arc>, err: Report) { sink.write() .await .submit_command(ACTION_SHOW_ERROR_DIALOG, SingleUse::new(err), Target::Auto) .expect("failed to send command"); } async fn handle_action( event_sink: Arc>, action_queue: Arc>>, ) { while let Some(action) = action_queue.write().await.recv().await { let event_sink = event_sink.clone(); match action { AsyncAction::DeployMods(state) => tokio::spawn(async move { if let Err(err) = deploy_mods(state).await.wrap_err("Failed to deploy mods") { tracing::error!("{:?}", err); send_error(event_sink.clone(), err).await; } event_sink .write() .await .submit_command(ACTION_FINISH_DEPLOY, (), Target::Auto) .expect("failed to send command"); }), AsyncAction::AddMod(state, info) => tokio::spawn(async move { match import_mod(state, info) .await .wrap_err("Failed to import mod") { Ok(mod_info) => { event_sink .write() .await .submit_command( ACTION_FINISH_ADD_MOD, SingleUse::new(Arc::new(mod_info)), Target::Auto, ) .expect("failed to send command"); } Err(err) => { tracing::error!("{:?}", err); send_error(event_sink.clone(), err).await; } } }), AsyncAction::DeleteMod(state, info) => tokio::spawn(async move { let mod_dir = state.mod_dir.join(&info.id); if let Err(err) = delete_mod(state, &info) .await .wrap_err("Failed to delete mod files") .with_suggestion(|| { format!("Clean the folder '{}' manually", mod_dir.display()) }) { tracing::error!("{:?}", err); send_error(event_sink.clone(), err).await; } event_sink .write() .await .submit_command( ACTION_FINISH_DELETE_SELECTED_MOD, SingleUse::new(info), Target::Auto, ) .expect("failed to send command"); }), AsyncAction::ResetDeployment(state) => tokio::spawn(async move { if let Err(err) = reset_mod_deployment(state) .await .wrap_err("Failed to reset mod deployment") { tracing::error!("{:?}", err); send_error(event_sink.clone(), err).await; } event_sink .write() .await .submit_command(ACTION_FINISH_RESET_DEPLOYMENT, (), Target::Auto) .expect("failed to send command"); }), AsyncAction::SaveSettings(state) => tokio::spawn(async move { if let Err(err) = save_settings(state) .await .wrap_err("Failed to save settings") { tracing::error!("{:?}", err); send_error(event_sink.clone(), err).await; } event_sink .write() .await .submit_command(ACTION_FINISH_SAVE_SETTINGS, (), Target::Auto) .expect("failed to send command"); }), AsyncAction::CheckUpdates(state) => tokio::spawn(async move { let updates = match check_updates(state) .await .wrap_err("Failed to check for updates") { Ok(updates) => updates, Err(err) => { tracing::error!("{:?}", err); send_error(event_sink.clone(), err).await; vec![] } }; event_sink .write() .await .submit_command( ACTION_FINISH_CHECK_UPDATE, SingleUse::new(updates), Target::Auto, ) .expect("failed to send command"); }), AsyncAction::LoadInitial((path, is_default)) => tokio::spawn(async move { let data = match load_initial(path, is_default) .await .wrap_err("Failed to load initial application data") { Ok(data) => Some(data), Err(err) => { tracing::error!("{:?}", err); send_error(event_sink.clone(), err).await; None } }; event_sink .write() .await .submit_command( ACTION_FINISH_LOAD_INITIAL, SingleUse::new(data), Target::Auto, ) .expect("failed to send command"); }), }; } } async fn handle_log( event_sink: Arc>, log_queue: Arc>>>, ) { while let Some(line) = log_queue.write().await.recv().await { let event_sink = event_sink.clone(); event_sink .write() .await .submit_command(ACTION_LOG, SingleUse::new(line), Target::Auto) .expect("failed to send command"); } } pub(crate) fn work_thread( event_sink: Arc>, action_queue: Arc>>, log_queue: Arc>>>, ) -> Result<()> { let rt = Runtime::new()?; rt.block_on(async { loop { tokio::select! { _ = handle_action(event_sink.clone(), action_queue.clone()) => {}, _ = handle_log(event_sink.clone(), log_queue.clone()) => {}, } } }); Ok(()) }