egui/file_handler: run parsing in file loader thread

- also embed I/O errors with other errors
This commit is contained in:
Alain Emilia Anna Zscheile 2024-09-30 11:55:53 +02:00
parent 1a46504dd5
commit 25a692aab9
4 changed files with 69 additions and 34 deletions

View File

@ -9,8 +9,11 @@ use std::{
use unic_langid::{langid, LanguageIdentifier}; use unic_langid::{langid, LanguageIdentifier};
use topola::{ use topola::{
autorouter::{invoker::Invoker, Autorouter}, autorouter::{history::History, invoker::Invoker, Autorouter},
specctra::{design::SpecctraDesign, mesadata::SpecctraMesadata}, specctra::{
design::{LoadingError as SpecctraLoadingError, SpecctraDesign},
mesadata::SpecctraMesadata,
},
stepper::Step, stepper::Step,
}; };
@ -38,12 +41,12 @@ pub struct App {
maybe_activity: Option<ActivityStepperWithStatus>, maybe_activity: Option<ActivityStepperWithStatus>,
content_channel: ( content_channel: (
Sender<std::io::Result<FileHandlerData>>, Sender<Result<SpecctraDesign, SpecctraLoadingError>>,
Receiver<std::io::Result<FileHandlerData>>, Receiver<Result<SpecctraDesign, SpecctraLoadingError>>,
), ),
history_channel: ( history_channel: (
Sender<std::io::Result<FileHandlerData>>, Sender<std::io::Result<Result<History, serde_json::Error>>>,
Receiver<std::io::Result<FileHandlerData>>, Receiver<std::io::Result<Result<History, serde_json::Error>>>,
), ),
viewport: Viewport, viewport: Viewport,
@ -109,13 +112,23 @@ impl App {
fn update_state(&mut self) -> bool { fn update_state(&mut self) -> bool {
if let Ok(data) = self.content_channel.1.try_recv() { if let Ok(data) = self.content_channel.1.try_recv() {
match data { match data {
Ok(data) => match self.load_specctra_dsn(data) { Ok(design) => match self.load_specctra_dsn(design) {
Ok(()) => {} Ok(()) => {}
Err(err) => { Err(err) => {
self.error_dialog.push_error("tr-module-specctra-dsn-file-loader", err); self.error_dialog.push_error("tr-module-specctra-dsn-file-loader", err);
} }
}, },
Err(err) => { Err(SpecctraLoadingError::Parse(err)) => {
self.error_dialog.push_error(
"tr-module-specctra-dsn-file-loader",
format!(
"{}; {}",
self.translator.text("tr-error_failed-to-parse-as-specctra-dsn"),
err
),
);
}
Err(SpecctraLoadingError::Io(err)) => {
self.error_dialog.push_error( self.error_dialog.push_error(
"tr-module-specctra-dsn-file-loader", "tr-module-specctra-dsn-file-loader",
format!("{}; {}", self.translator.text("tr-error_unable-to-read-file"), err), format!("{}; {}", self.translator.text("tr-error_unable-to-read-file"), err),
@ -125,18 +138,18 @@ impl App {
} }
if let Some(invoker) = self.arc_mutex_maybe_invoker.lock().unwrap().as_mut() { if let Some(invoker) = self.arc_mutex_maybe_invoker.lock().unwrap().as_mut() {
if let Ok(input) = self.history_channel.1.try_recv() { if let Ok(data) = self.history_channel.1.try_recv() {
let tr = &self.translator; let tr = &self.translator;
match input { match data {
Ok(bufread) => match serde_json::from_reader(bufread) { Ok(Ok(data)) => {
Ok(res) => invoker.replay(res), invoker.replay(data);
Err(err) => { }
Ok(Err(err)) => {
self.error_dialog.push_error( self.error_dialog.push_error(
"tr-module-history-file-loader", "tr-module-history-file-loader",
format!("{}; {}", tr.text("tr-error_failed-to-parse-as-history-json"), err), format!("{}; {}", tr.text("tr-error_failed-to-parse-as-history-json"), err),
); );
} }
},
Err(err) => { Err(err) => {
self.error_dialog.push_error( self.error_dialog.push_error(
"tr-module-history-file-loader", "tr-module-history-file-loader",
@ -161,10 +174,8 @@ impl App {
false false
} }
fn load_specctra_dsn(&mut self, bufread: FileHandlerData) -> Result<(), String> { fn load_specctra_dsn(&mut self, design: SpecctraDesign) -> Result<(), String> {
let tr = &self.translator; let tr = &self.translator;
let design = SpecctraDesign::load(bufread)
.map_err(|err| format!("{}; {}", tr.text("tr-error_failed-to-parse-as-specctra-dsn"), err))?;
let board = design.make_board(); let board = design.make_board();
let overlay = Overlay::new(&board) let overlay = Overlay::new(&board)
.map_err(|err| format!("{}; {}", tr.text("tr-error_unable-to-initialize-overlay"), err))?; .map_err(|err| format!("{}; {}", tr.text("tr-error_unable-to-initialize-overlay"), err))?;

View File

@ -46,19 +46,28 @@ impl io::BufRead for FileHandlerData {
fhd_forward!(fn consume(&mut self, amt: usize)); fhd_forward!(fn consume(&mut self, amt: usize));
} }
pub async fn push_file_to_read( #[inline]
pub async fn push_file_to_read<R, E, C>(
file_handle: &rfd::FileHandle, file_handle: &rfd::FileHandle,
sender: Sender<std::io::Result<FileHandlerData>>, sender: Sender<Result<R, E>>,
) { callback: C,
let _ = sender.send(handle_text(&file_handle).await); ) where
E: From<std::io::Error>,
C: FnOnce(FileHandlerData) -> Result<R, E>,
{
let _ = sender.send(handle_text(&file_handle, callback).await);
} }
async fn handle_text(file_handle: &rfd::FileHandle) -> std::io::Result<FileHandlerData> { async fn handle_text<R, E, C>(file_handle: &rfd::FileHandle, callback: C) -> Result<R, E>
where
E: From<std::io::Error>,
C: FnOnce(FileHandlerData) -> Result<R, E>,
{
#[cfg(not(target_arch = "wasm32"))] #[cfg(not(target_arch = "wasm32"))]
let res = FileHandlerData::File(io::BufReader::new(std::fs::File::open(file_handle.path())?)); let res = FileHandlerData::File(io::BufReader::new(std::fs::File::open(file_handle.path())?));
#[cfg(target_arch = "wasm32")] #[cfg(target_arch = "wasm32")]
let res = FileHandlerData::Contents(io::Cursor::new(file_handle.read().await)); let res = FileHandlerData::Contents(io::Cursor::new(file_handle.read().await));
Ok(res) callback(res)
} }

View File

@ -6,11 +6,15 @@ use std::{
use topola::{ use topola::{
autorouter::{ autorouter::{
command::Command, command::Command,
history::History,
invoker::{Invoker, InvokerError}, invoker::{Invoker, InvokerError},
AutorouterOptions, AutorouterOptions,
}, },
router::RouterOptions, router::RouterOptions,
specctra::{design::SpecctraDesign, mesadata::SpecctraMesadata}, specctra::{
design::{LoadingError as SpecctraLoadingError, SpecctraDesign},
mesadata::SpecctraMesadata,
},
stepper::Abort, stepper::Abort,
}; };
@ -59,8 +63,8 @@ impl MenuBar {
&mut self, &mut self,
ctx: &egui::Context, ctx: &egui::Context,
tr: &Translator, tr: &Translator,
content_sender: Sender<std::io::Result<FileHandlerData>>, content_sender: Sender<Result<SpecctraDesign, SpecctraLoadingError>>,
history_sender: Sender<std::io::Result<FileHandlerData>>, history_sender: Sender<std::io::Result<Result<History, serde_json::Error>>>,
arc_mutex_maybe_invoker: Arc<Mutex<Option<Invoker<SpecctraMesadata>>>>, arc_mutex_maybe_invoker: Arc<Mutex<Option<Invoker<SpecctraMesadata>>>>,
maybe_activity: &mut Option<ActivityStepperWithStatus>, maybe_activity: &mut Option<ActivityStepperWithStatus>,
viewport: &mut Viewport, viewport: &mut Viewport,
@ -239,7 +243,10 @@ impl MenuBar {
execute(async move { execute(async move {
if let Some(file_handle) = task.await { if let Some(file_handle) = task.await {
push_file_to_read(&file_handle, content_sender).await; push_file_to_read(&file_handle, content_sender, |data| {
SpecctraDesign::load(data)
})
.await;
ctx.request_repaint(); ctx.request_repaint();
} }
}); });
@ -278,7 +285,14 @@ impl MenuBar {
execute(async move { execute(async move {
if let Some(file_handle) = task.await { if let Some(file_handle) = task.await {
push_file_to_read(&file_handle, history_sender).await; push_file_to_read(&file_handle, history_sender, |data| {
match serde_json::from_reader(data) {
Ok(history) => Ok(Ok(history)),
Err(err) if err.is_io() => Err(err.into()),
Err(err) => Ok(Err(err)),
}
})
.await;
ctx.request_repaint(); ctx.request_repaint();
} }
}); });

View File

@ -26,9 +26,10 @@ use crate::{
}, },
}; };
#[derive(Error, Debug)] pub use read::ParseErrorContext;
/// Errors raised by [`SpecctraDesign::load`] /// Errors raised by [`SpecctraDesign::load`]
#[derive(Error, Debug)]
pub enum LoadingError { pub enum LoadingError {
/// I/O file reading error from [`std::io::Error`] /// I/O file reading error from [`std::io::Error`]
#[error(transparent)] #[error(transparent)]