diff --git a/frontend/src/platform/browser.rs b/frontend/src/platform/browser.rs index 0f6b7d7..56fc6d2 100644 --- a/frontend/src/platform/browser.rs +++ b/frontend/src/platform/browser.rs @@ -111,7 +111,9 @@ pub(super) async fn load_signal_and_get_timeline( timeline_viewport_x, block_height, var_format, - ); + |value| Box::pin(async { value }), + ) + .await; timeline } diff --git a/shared/src/signal_to_timeline.rs b/shared/src/signal_to_timeline.rs index f2d05b6..9aa1696 100644 --- a/shared/src/signal_to_timeline.rs +++ b/shared/src/signal_to_timeline.rs @@ -1,13 +1,25 @@ use crate::*; +use future::BoxFuture; +use wellen::SignalValue; -pub fn signal_to_timeline( - signal: &wellen::Signal, +// @TODO remove once https://github.com/rust-lang/rust/issues/89976 is resolved +// (`error: implementation of `FnOnce` is not general enough`) +fn type_hint(f: F) -> F +where + F: for<'a> FnMut((u32, SignalValue<'a>)) -> (f64, SignalValue<'a>), +{ + f +} + +pub async fn signal_to_timeline<'s>( + signal: &'s wellen::Signal, time_table: &[wellen::Time], timeline_zoom: f64, timeline_viewport_width: u32, timeline_viewport_x: i32, block_height: u32, var_format: VarFormat, + mut format_by_decoders: impl FnMut(String) -> BoxFuture<'s, String>, ) -> Timeline { const MIN_BLOCK_WIDTH: u32 = 3; // Courier New, 16px, sync with `label_style` in `pixi_canvas.rs` @@ -25,12 +37,12 @@ pub fn signal_to_timeline( let mut x_value_pairs = signal .iter_changes() - .map(|(index, value)| { + .map(type_hint(move |(index, value)| { let index = index as usize; let time = time_table[index] as f64; let x = time / last_time * timeline_width - timeline_viewport_x; (x, value) - }) + })) .peekable(); // @TODO parallelize? @@ -55,7 +67,8 @@ pub fn signal_to_timeline( } // @TODO cache? - let value = var_format.format(value); + let mut value = var_format.format(value); + value = format_by_decoders(value).await; let value_width = (value.chars().count() as f64 * LETTER_WIDTH) as u32; // @TODO Ellipsis instead of hiding? diff --git a/src-tauri/src/component_manager.rs b/src-tauri/src/component_manager.rs index 3e91edb..1055f30 100644 --- a/src-tauri/src/component_manager.rs +++ b/src-tauri/src/component_manager.rs @@ -1,14 +1,14 @@ use crate::{AddedDecodersCount, DecoderPath, RemovedDecodersCount}; use once_cell::sync::Lazy; use std::sync::Arc; -use tauri::async_runtime::Mutex; +use tauri::async_runtime::{Mutex, RwLock}; use wasmtime::component::{Component as WasmtimeComponent, *}; use wasmtime::{AsContextMut, Engine, Store}; use wasmtime_wasi::{WasiCtx, WasiView}; bindgen!(); -static DECODERS: Lazy>>> = Lazy::new(<_>::default); +pub static DECODERS: Lazy>>> = Lazy::new(<_>::default); static ENGINE: Lazy = Lazy::new(<_>::default); static LINKER: Lazy> = Lazy::new(|| { let mut linker = Linker::new(&ENGINE); @@ -16,7 +16,7 @@ static LINKER: Lazy> = Lazy::new(|| { Component::add_to_linker(&mut linker, |state: &mut State| state).unwrap(); linker }); -static STORE: Lazy>>> = Lazy::new(|| { +pub static STORE: Lazy>>> = Lazy::new(|| { let store = Store::new( &ENGINE, State { @@ -27,7 +27,7 @@ static STORE: Lazy>>> = Lazy::new(|| { Arc::new(Mutex::new(store)) }); -struct State { +pub struct State { ctx: WasiCtx, table: ResourceTable, } @@ -48,7 +48,7 @@ impl component::decoder::host::Host for State { } pub async fn remove_all_decoders() -> RemovedDecodersCount { - let mut decoders = DECODERS.lock().await; + let mut decoders = DECODERS.write().await; let decoders_count = decoders.len(); decoders.clear(); decoders_count @@ -98,7 +98,7 @@ async fn add_decoder(path: &str) -> wasmtime::Result<()> { .component_decoder_decoder() .call_init(&mut store)?; - DECODERS.lock().await.push(component); + DECODERS.write().await.push(component); Ok(()) } diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index 9dc98c6..9a2cb18 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -1,6 +1,7 @@ use std::fs; -use std::sync::Mutex; +use tauri::async_runtime::RwLock; use tauri_plugin_dialog::DialogExt; +use wasmtime::AsContextMut; use wellen::simple::Waveform; type Filename = String; @@ -13,7 +14,7 @@ mod component_manager; #[derive(Default)] struct Store { - waveform: Mutex>, + waveform: RwLock>, } #[tauri::command(rename_all = "snake_case")] @@ -35,7 +36,7 @@ async fn pick_and_load_waveform( let Ok(waveform) = waveform else { panic!("Waveform file reading failed") }; - *store.waveform.lock().unwrap() = Some(waveform); + *store.waveform.write().await = Some(waveform); Ok(Some(file_response.name.unwrap())) } @@ -53,8 +54,9 @@ async fn load_file_with_selected_vars(app: tauri::AppHandle) -> Result) -> Result { - let waveform = store.waveform.lock().unwrap(); - let hierarchy = waveform.as_ref().unwrap().hierarchy(); + let waveform_lock = store.waveform.read().await; + let waveform = waveform_lock.as_ref().unwrap(); + let hierarchy = waveform.hierarchy(); Ok(serde_json::to_value(hierarchy).unwrap()) } @@ -70,7 +72,7 @@ async fn load_signal_and_get_timeline( ) -> Result { // @TODO run (all?) in a blocking thread? let signal_ref = wellen::SignalRef::from_index(signal_ref_index).unwrap(); - let mut waveform_lock = store.waveform.lock().unwrap(); + let mut waveform_lock = store.waveform.write().await; let waveform = waveform_lock.as_mut().unwrap(); waveform.load_signals_multi_threaded(&[signal_ref]); let signal = waveform.get_signal(signal_ref).unwrap(); @@ -83,14 +85,29 @@ async fn load_signal_and_get_timeline( timeline_viewport_x, block_height, var_format, - ); + |mut value: String| { + Box::pin(async { + let decoders = component_manager::DECODERS.read().await; + let mut store_lock = component_manager::STORE.lock().await; + let mut store = store_lock.as_context_mut(); + for decoder in decoders.iter() { + value = decoder + .component_decoder_decoder() + .call_format_signal_value(&mut store, &value) + .unwrap() + } + value + }) + }, + ) + .await; Ok(serde_json::to_value(timeline).unwrap()) } #[tauri::command(rename_all = "snake_case")] async fn unload_signal(signal_ref_index: usize, store: tauri::State<'_, Store>) -> Result<(), ()> { let signal_ref = wellen::SignalRef::from_index(signal_ref_index).unwrap(); - let mut waveform_lock = store.waveform.lock().unwrap(); + let mut waveform_lock = store.waveform.write().await; let waveform = waveform_lock.as_mut().unwrap(); waveform.unload_signals(&[signal_ref]); Ok(())