diff --git a/src/bin/topola-egui/app.rs b/src/bin/topola-egui/app.rs index 149be86..2b09140 100644 --- a/src/bin/topola-egui/app.rs +++ b/src/bin/topola-egui/app.rs @@ -18,7 +18,7 @@ use crate::{ activity::{ActivityStatus, ActivityStepperWithStatus}, config::Config, error_dialog::ErrorDialog, - file_receiver::FileReceiver, + file_handler::FileHandlerData, layers::Layers, menu_bar::MenuBar, overlay::Overlay, @@ -37,8 +37,14 @@ pub struct App { maybe_activity: Option, - content_channel: (Sender, Receiver), - history_channel: (Sender, Receiver), + content_channel: ( + Sender>, + Receiver>, + ), + history_channel: ( + Sender>, + Receiver>, + ), viewport: Viewport, @@ -101,21 +107,25 @@ impl App { } fn update_state(&mut self) -> bool { - let mut content_file_receiver = FileReceiver::new(&self.content_channel.1); - - if let Some(input) = content_file_receiver.try_recv() { - match self.load_specctra_dsn(input) { - Ok(()) => {} + if let Ok(data) = self.content_channel.1.try_recv() { + match data { + Ok(data) => match self.load_specctra_dsn(data) { + Ok(()) => {} + Err(err) => { + self.error_dialog.push_error("tr-module-specctra-dsn-file-loader", 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", + format!("{}; {}", self.translator.text("tr-error_unable-to-read-file"), err), + ); } } } if let Some(invoker) = self.arc_mutex_maybe_invoker.lock().unwrap().as_mut() { - let mut history_file_receiver = FileReceiver::new(&self.history_channel.1); - - if let Some(input) = history_file_receiver.try_recv() { + if let Ok(input) = self.history_channel.1.try_recv() { let tr = &self.translator; match input { Ok(bufread) => match serde_json::from_reader(bufread) { @@ -151,12 +161,8 @@ impl App { false } - fn load_specctra_dsn( - &mut self, - input: std::io::Result, - ) -> Result<(), String> { + fn load_specctra_dsn(&mut self, bufread: FileHandlerData) -> Result<(), String> { let tr = &self.translator; - let bufread = input.map_err(|err| format!("{}; {}", tr.text("tr-error_unable-to-read-file"), err))?; 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(); diff --git a/src/bin/topola-egui/file_handler.rs b/src/bin/topola-egui/file_handler.rs new file mode 100644 index 0000000..9f94c50 --- /dev/null +++ b/src/bin/topola-egui/file_handler.rs @@ -0,0 +1,64 @@ +use std::io; +use std::sync::mpsc::{SendError, Sender}; + +#[repr(transparent)] +pub enum FileHandlerData { + #[cfg(not(target_arch = "wasm32"))] + File(io::BufReader), + + #[cfg(target_arch = "wasm32")] + Contents(io::Cursor>), +} + +macro_rules! fhd_forward { + (match $self:expr => $fname:ident($($arg:expr,)*)) => {{ + match $self { + #[cfg(not(target_arch = "wasm32"))] + FileHandlerData::File(brf) => brf.$fname($($arg,)*), + + #[cfg(target_arch = "wasm32")] + FileHandlerData::Contents(curs) => curs.$fname($($arg,)*), + } + }}; + + (fn $fname:ident(&mut self $(,$arg:ident : $argty:ty)*)) => { + #[inline] + fn $fname(&mut self $(,$arg : $argty)*) { + fhd_forward!(match self => $fname($($arg,)*)) + } + }; + (fn $fname:ident(&mut self $(,$arg:ident : $argty:ty)*) -> $ret:ty) => { + #[inline] + fn $fname(&mut self $(,$arg : $argty)*) -> $ret { + fhd_forward!(match self => $fname($($arg,)*)) + } + } +} + +impl io::Read for FileHandlerData { + fhd_forward!(fn read(&mut self, buf: &mut [u8]) -> io::Result); + fhd_forward!(fn read_to_end(&mut self, buf: &mut Vec) -> io::Result); + fhd_forward!(fn read_vectored(&mut self, bufs: &mut [io::IoSliceMut<'_>]) -> io::Result); +} + +impl io::BufRead for FileHandlerData { + fhd_forward!(fn fill_buf(&mut self) -> io::Result<&[u8]>); + fhd_forward!(fn consume(&mut self, amt: usize)); +} + +pub async fn push_file_to_read( + file_handle: &rfd::FileHandle, + sender: Sender>, +) { + let _ = sender.send(handle_text(&file_handle).await); +} + +async fn handle_text(file_handle: &rfd::FileHandle) -> std::io::Result { + #[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) +} diff --git a/src/bin/topola-egui/file_receiver.rs b/src/bin/topola-egui/file_receiver.rs deleted file mode 100644 index f5e6b1b..0000000 --- a/src/bin/topola-egui/file_receiver.rs +++ /dev/null @@ -1,22 +0,0 @@ -use std::io::{BufReader, Cursor}; -use std::sync::mpsc::Receiver; - -pub struct FileReceiver<'a> { - receiver: &'a Receiver, -} - -impl<'a> FileReceiver<'a> { - pub fn new(receiver: &'a Receiver) -> Self { - Self { receiver } - } - - #[cfg(not(target_arch = "wasm32"))] - pub fn try_recv(&mut self) -> Option, std::io::Error>> { - Some(std::fs::File::open(self.receiver.try_recv().ok()?).map(std::io::BufReader::new)) - } - - #[cfg(target_arch = "wasm32")] - pub fn try_recv(&mut self) -> Option>, std::io::Error>> { - Some(Ok(Cursor::new(self.receiver.try_recv().ok()?.into()))) - } -} diff --git a/src/bin/topola-egui/file_sender.rs b/src/bin/topola-egui/file_sender.rs deleted file mode 100644 index c1ed29a..0000000 --- a/src/bin/topola-egui/file_sender.rs +++ /dev/null @@ -1,27 +0,0 @@ -use std::sync::mpsc::{SendError, Sender}; - -pub struct FileSender { - sender: Sender, -} - -impl FileSender { - pub fn new(sender: Sender) -> Self { - Self { sender } - } - - pub async fn send(&self, file_handle: rfd::FileHandle) -> Result<(), SendError> { - self.sender.send(self.handle_text(&file_handle).await) - } - - #[cfg(not(target_arch = "wasm32"))] - async fn handle_text(&self, file_handle: &rfd::FileHandle) -> String { - file_handle.path().to_str().unwrap().to_string() - } - - #[cfg(target_arch = "wasm32")] - async fn handle_text(&self, file_handle: &rfd::FileHandle) -> String { - std::str::from_utf8(&file_handle.read().await) - .unwrap() - .to_string() - } -} diff --git a/src/bin/topola-egui/main.rs b/src/bin/topola-egui/main.rs index 7b585fd..aac10cb 100644 --- a/src/bin/topola-egui/main.rs +++ b/src/bin/topola-egui/main.rs @@ -5,8 +5,7 @@ mod activity; mod app; mod config; mod error_dialog; -mod file_receiver; -mod file_sender; +mod file_handler; mod layers; mod menu_bar; mod overlay; diff --git a/src/bin/topola-egui/menu_bar.rs b/src/bin/topola-egui/menu_bar.rs index e096858..e689b4d 100644 --- a/src/bin/topola-egui/menu_bar.rs +++ b/src/bin/topola-egui/menu_bar.rs @@ -18,7 +18,7 @@ use crate::{ action::{Action, Switch, Trigger}, activity::{ActivityStatus, ActivityStepperWithStatus}, app::execute, - file_sender::FileSender, + file_handler::{push_file_to_read, FileHandlerData}, overlay::Overlay, translator::Translator, viewport::Viewport, @@ -59,8 +59,8 @@ impl MenuBar { &mut self, ctx: &egui::Context, tr: &Translator, - content_sender: Sender, - history_sender: Sender, + content_sender: Sender>, + history_sender: Sender>, arc_mutex_maybe_invoker: Arc>>>, maybe_activity: &mut Option, viewport: &mut Viewport, @@ -239,8 +239,7 @@ impl MenuBar { execute(async move { if let Some(file_handle) = task.await { - let file_sender = FileSender::new(content_sender); - file_sender.send(file_handle).await; + push_file_to_read(&file_handle, content_sender).await; ctx.request_repaint(); } }); @@ -279,8 +278,7 @@ impl MenuBar { execute(async move { if let Some(file_handle) = task.await { - let file_sender = FileSender::new(history_sender); - file_sender.send(file_handle).await; + push_file_to_read(&file_handle, history_sender).await; ctx.request_repaint(); } });