From 726b919163263f464a710df34210db79bfa250eb Mon Sep 17 00:00:00 2001 From: Ross Andrews Date: Wed, 26 Nov 2025 00:08:41 -0600 Subject: [PATCH] async --- src/fleen_app.rs | 49 +++++++++++++++++------------------------------- src/main.rs | 42 +++++++++++++++++++++++++---------------- 2 files changed, 43 insertions(+), 48 deletions(-) diff --git a/src/fleen_app.rs b/src/fleen_app.rs index 7877277..18d30c8 100644 --- a/src/fleen_app.rs +++ b/src/fleen_app.rs @@ -1,13 +1,11 @@ -use std::cell::RefCell; use std::{fs, io, time}; use std::path::{Path, PathBuf}; use std::process::Command; +use std::sync::RwLock; use clipboard_rs::Clipboard; use clipboard_rs::common::RustImage; -use tempfile::TempDir; use thiserror::Error; use tinyrand::{Rand, Seeded}; -use tokio::task::JoinHandle; use crate::fleen_app::FleenError::{RootDirNonexistence, RootDirPopulated, TargetDir}; use crate::fleen_app::TreeEntry::{CloseDir, Dir}; use crate::renderer; @@ -55,13 +53,13 @@ pub enum FileType { pub struct FleenApp { root: PathBuf, - files_cache: RefCell>> + files_cache: RwLock>> } impl FleenApp { pub fn open(root: PathBuf) -> Result { match root.try_exists() { - Ok(true) => Ok(Self { root, files_cache: RefCell::new(None) }), + Ok(true) => Ok(Self { root, files_cache: RwLock::new(None) }), _ => Err(RootDirNonexistence(root)) } } @@ -73,7 +71,7 @@ impl FleenApp { Err(RootDirPopulated(root)) } else { Self::initialize_site(root.clone()).map_err(|e| FleenError::FileIo(String::from("creating site"), e.to_string()))?; - Ok(Self { root, files_cache: RefCell::new(None) }) + Ok(Self { root, files_cache: RwLock::new(None) }) } } Err(_) => { @@ -96,7 +94,7 @@ impl FleenApp { // TODO: This is panicky as hell, make it return a Result fn refresh_file_cache(&self, force: bool) { - if self.files_cache.borrow().is_none() || force { + if self.files_cache.read().unwrap().is_none() || force { let mut entries = vec![]; fn visit_dir(dir: &Path, entries: &mut Vec) { @@ -117,13 +115,14 @@ impl FleenApp { visit_dir(&self.root, &mut entries); entries.push(CloseDir); - self.files_cache.replace(Some(entries)); + self.files_cache.write().unwrap().replace(entries); } } pub fn file_tree_entries(&self) -> impl IntoIterator { self.refresh_file_cache(false); - self.files_cache.borrow().clone().expect("Can't happen because we just refreshed the cache") + // We need to clone these because the rwlock owns them + self.files_cache.read().unwrap().clone().expect("Can't happen because we just refreshed the cache") } pub fn open_filename(&self, filename: &str) -> Result<(), FleenError> { @@ -289,7 +288,7 @@ impl FleenApp { Ok(()) } - pub fn build_and_deploy(&self) -> Result>, FleenError> { + pub async fn build_and_deploy(&self) -> Result { let output_dir = tempfile::tempdir().map_err(|_| TargetDir)?; self.build_site(&output_dir.path())?; // Attempt to build the site somewhere @@ -299,30 +298,16 @@ impl FleenApp { } else { let mut command = Command::new(deploy_script_path); command.current_dir(output_dir.path()); // don't consume dir! - Ok(self.build_and_deploy_inner(output_dir, command)) - } - } + let output = command.output().map_err(|e| FleenError::DeployError(e.to_string()))?; + let status = command.status()?; - fn build_and_deploy_inner(&self, output_dir: TempDir, mut command: Command) -> JoinHandle> { - tokio::spawn(async move { - let output = command.output(); - let status = command.status().unwrap(); - output_dir.close().map_err(|e| FleenError::Io(e))?; - - match output { - Ok(output) => { - let output = String::from_utf8(output.stdout).unwrap_or("Error reading deploy script output".to_string()); - if status.success() { - Ok(output) - } else { - Err(FleenError::DeployError(output)) - } - }, - Err(e) => { - Err(FleenError::DeployError(e.to_string())) - } + let output = String::from_utf8(output.stdout).unwrap_or("Error reading deploy script output".to_string()); + if status.success() { + Ok(output) + } else { + Err(FleenError::DeployError(output)) } - }) + } } } diff --git a/src/main.rs b/src/main.rs index fd3990d..c158022 100644 --- a/src/main.rs +++ b/src/main.rs @@ -4,7 +4,7 @@ mod server; mod ui_ext; use std::path::{Path, PathBuf}; -use std::sync::Arc; +use std::sync::{Arc, Mutex, MutexGuard}; use std::task::Waker; use std::time::{Duration, Instant}; use eframe::egui::{Button, Context, Id, RichText}; @@ -46,7 +46,8 @@ struct FleenUi { dialog_mode: Option, server_handle: Option>, server_port: String, - deploy_handle: Option>>, + deploy_response: Arc>>>, + deploying: bool, image_message: Option, } @@ -59,7 +60,8 @@ impl Default for FleenUi { selected_file: None, dialog_mode: None, server_handle: None, - deploy_handle: None, + deploy_response: Mutex::new(None).into(), + deploying: false, image_message: None, server_port: "3000".to_string(), } @@ -109,8 +111,13 @@ impl FleenUi { }); ui.column(width, |ui| self.server_controls(ui)); ui.column(width, |ui| { - ui.add_enabled_ui(self.deploy_handle.is_none(), |ui| { - if ui.add_fill_width(Button::green("Build and deploy")).clicked() { + ui.add_enabled_ui(!self.deploying, |ui| { + let label = if self.deploying { + "Deploying..." + } else { + "Build and Deploy" + }; + if ui.add_fill_width(Button::green(label)).clicked() { self.build_and_deploy(); } }); @@ -135,10 +142,15 @@ impl FleenUi { } fn build_and_deploy(&mut self) { - match self.app.as_ref().unwrap().build_and_deploy() { - Ok(handle) => { self.deploy_handle = Some(handle) }, - Err(err) => { self.error = Some(err) } - } + self.deploying = true; + let mutex = self.deploy_response.clone(); + let app = self.app.as_ref().unwrap().clone(); + tokio::spawn(async move { + let result = app.build_and_deploy().await; + if let Ok(mut m) = mutex.lock() { + *m = Some(result); + } + }); } fn tree_view(&mut self, ui: &mut egui::Ui) { @@ -361,15 +373,13 @@ impl FleenUi { impl eframe::App for FleenUi { fn update(&mut self, ctx: &Context, _frame: &mut Frame) { - if let Some(handle) = self.deploy_handle.as_ref() { - if handle.is_finished() { - let handle = self.deploy_handle.take().unwrap(); - let result = futures::executor::block_on(async move { handle.await }); + if self.deploying && let Ok(mut m) = self.deploy_response.lock() { + if let Some(result) = m.take() { match result { - Err(e) => { self.error = Some(FleenError::DeployError(e.to_string())) } - Ok(Err(e)) => { self.error = Some(e) } - Ok(Ok(s)) => { self.message = Some(s) } + Err(e) => { self.error = Some(e) } + Ok(s) => { self.message = Some(s) } } + self.deploying = false; } ctx.request_repaint_after(Duration::from_millis(100)); }