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 topola::{
autorouter::{invoker::Invoker, Autorouter},
specctra::{design::SpecctraDesign, mesadata::SpecctraMesadata},
autorouter::{history::History, invoker::Invoker, Autorouter},
specctra::{
design::{LoadingError as SpecctraLoadingError, SpecctraDesign},
mesadata::SpecctraMesadata,
},
stepper::Step,
};
@ -38,12 +41,12 @@ pub struct App {
maybe_activity: Option<ActivityStepperWithStatus>,
content_channel: (
Sender<std::io::Result<FileHandlerData>>,
Receiver<std::io::Result<FileHandlerData>>,
Sender<Result<SpecctraDesign, SpecctraLoadingError>>,
Receiver<Result<SpecctraDesign, SpecctraLoadingError>>,
),
history_channel: (
Sender<std::io::Result<FileHandlerData>>,
Receiver<std::io::Result<FileHandlerData>>,
Sender<std::io::Result<Result<History, serde_json::Error>>>,
Receiver<std::io::Result<Result<History, serde_json::Error>>>,
),
viewport: Viewport,
@ -109,13 +112,23 @@ impl App {
fn update_state(&mut self) -> bool {
if let Ok(data) = self.content_channel.1.try_recv() {
match data {
Ok(data) => match self.load_specctra_dsn(data) {
Ok(design) => match self.load_specctra_dsn(design) {
Ok(()) => {}
Err(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(
"tr-module-specctra-dsn-file-loader",
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 Ok(input) = self.history_channel.1.try_recv() {
if let Ok(data) = self.history_channel.1.try_recv() {
let tr = &self.translator;
match input {
Ok(bufread) => match serde_json::from_reader(bufread) {
Ok(res) => invoker.replay(res),
Err(err) => {
match data {
Ok(Ok(data)) => {
invoker.replay(data);
}
Ok(Err(err)) => {
self.error_dialog.push_error(
"tr-module-history-file-loader",
format!("{}; {}", tr.text("tr-error_failed-to-parse-as-history-json"), err),
);
}
},
Err(err) => {
self.error_dialog.push_error(
"tr-module-history-file-loader",
@ -161,10 +174,8 @@ impl App {
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 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 overlay = Overlay::new(&board)
.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));
}
pub async fn push_file_to_read(
#[inline]
pub async fn push_file_to_read<R, E, C>(
file_handle: &rfd::FileHandle,
sender: Sender<std::io::Result<FileHandlerData>>,
) {
let _ = sender.send(handle_text(&file_handle).await);
sender: Sender<Result<R, E>>,
callback: C,
) 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"))]
let res = FileHandlerData::File(io::BufReader::new(std::fs::File::open(file_handle.path())?));
#[cfg(target_arch = "wasm32")]
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::{
autorouter::{
command::Command,
history::History,
invoker::{Invoker, InvokerError},
AutorouterOptions,
},
router::RouterOptions,
specctra::{design::SpecctraDesign, mesadata::SpecctraMesadata},
specctra::{
design::{LoadingError as SpecctraLoadingError, SpecctraDesign},
mesadata::SpecctraMesadata,
},
stepper::Abort,
};
@ -59,8 +63,8 @@ impl MenuBar {
&mut self,
ctx: &egui::Context,
tr: &Translator,
content_sender: Sender<std::io::Result<FileHandlerData>>,
history_sender: Sender<std::io::Result<FileHandlerData>>,
content_sender: Sender<Result<SpecctraDesign, SpecctraLoadingError>>,
history_sender: Sender<std::io::Result<Result<History, serde_json::Error>>>,
arc_mutex_maybe_invoker: Arc<Mutex<Option<Invoker<SpecctraMesadata>>>>,
maybe_activity: &mut Option<ActivityStepperWithStatus>,
viewport: &mut Viewport,
@ -239,7 +243,10 @@ impl MenuBar {
execute(async move {
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();
}
});
@ -278,7 +285,14 @@ impl MenuBar {
execute(async move {
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();
}
});

View File

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