Improve error handling (#222)

* Introduce proper error types for a lot of functions, remove main catch-all type

* Fix compilation and clippy issues

* Map, unmap and drop always

* Remove not required parentheses

* Fix running on non-vulkan platforms

* Add and use thiserror crate
This commit is contained in:
Max Ammann 2022-12-12 20:06:12 +01:00 committed by GitHub
parent 5b084f033e
commit c12f3f1127
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
44 changed files with 821 additions and 674 deletions

View File

@ -4,7 +4,6 @@ use criterion::{criterion_group, criterion_main, Criterion};
use maplibre::{
benchmarking::io::static_tile_fetcher::StaticTileFetcher,
coords::{TileCoords, ZoomLevel},
error::Error,
io::{
pipeline::{PipelineContext, PipelineProcessor, Processable},
tile_pipelines::{ParseTile, TessellateLayer},

View File

@ -1,7 +1,6 @@
use criterion::{criterion_group, criterion_main, Criterion};
use maplibre::{
coords::{WorldTileCoords, ZoomLevel},
error::Error,
headless::{create_headless_renderer, map::HeadlessMap},
platform::run_multithreaded,
style::Style,
@ -15,32 +14,20 @@ fn headless_render(c: &mut Criterion) {
let map = HeadlessMap::new(style, renderer, kernel, false).unwrap();
let tile = map
.fetch_tile(
WorldTileCoords::from((0, 0, ZoomLevel::default())),
&["water"],
)
.fetch_tile(WorldTileCoords::from((0, 0, ZoomLevel::default())))
.await
.expect("Failed to fetch and process!");
.expect("Failed to fetch!");
let tile = map
.process_tile(tile, &["water"])
.await
.expect("Failed to process!");
(map, tile)
});
b.to_async(
tokio::runtime::Builder::new_multi_thread()
.enable_all()
.build()
.unwrap(),
)
.iter(|| {
match map.render_tile(tile.clone()) {
Ok(_) => {}
Err(Error::Render(e)) => {
eprintln!("{}", e);
if e.should_exit() {}
}
e => eprintln!("{:?}", e),
};
async {}
b.iter(|| {
map.render_tile(tile.clone());
});
});
}

View File

@ -32,17 +32,20 @@ pub async fn run_headless(tile_size: u32, min: LatLon, max: LatLon) {
for (z, x, y) in GridIterator::new(10, 10, tile_limits) {
let coords = WorldTileCoords::from((x as i32, y as i32, z.into()));
println!("Rendering {}", &coords);
let tile = map.fetch_tile(coords).await.expect("Failed to fetch!");
let tile = map
.fetch_tile(
coords,
.process_tile(
tile,
&requested_layers
.iter()
.map(|layer| layer.as_str())
.collect::<Vec<_>>(),
)
.await
.expect("Failed to fetch and process");
.expect("Failed to process!");
map.render_tile(tile).expect("Rendering failed");
map.render_tile(tile);
}
}

View File

@ -5,8 +5,7 @@ use std::{fmt::Debug, marker::PhantomData};
use instant::Instant;
use maplibre::{
environment::Environment,
error::Error,
event_loop::{EventLoop, EventLoopProxy},
event_loop::{EventLoop, EventLoopProxy, SendEventError},
io::{apc::AsyncProcedureCall, scheduler::Scheduler, source_client::HttpClient},
map::Map,
window::{HeadedMapWindow, MapWindowConfig},
@ -84,16 +83,10 @@ impl<ET: 'static + PartialEq + Debug> EventLoop<ET> for WinitEventLoop<ET> {
#[cfg(target_os = "android")]
if !map.has_renderer() && event == Event::Resumed {
use tokio::{runtime::Handle, task};
use maplibre::render::settings::WgpuSettings;
use maplibre::render::builder::RendererBuilder;
task::block_in_place(|| {
Handle::current().block_on(async {
map.initialize_renderer(RendererBuilder::new()
.with_wgpu_settings(WgpuSettings {
backends: Some(maplibre::render::settings::Backends::VULKAN), // FIXME: Change
..WgpuSettings::default()
})).await.unwrap();
map.initialize_renderer().await.unwrap();
})
});
return;
@ -146,16 +139,8 @@ impl<ET: 'static + PartialEq + Debug> EventLoop<ET> for WinitEventLoop<ET> {
input_controller.update_state(map_context, dt);
}
match map.run_schedule() {
Ok(_) => {}
Err(Error::Render(e)) => {
eprintln!("{}", e);
if e.should_exit() {
*control_flow = ControlFlow::Exit;
}
}
e => eprintln!("{:?}", e)
};
// TODO: Maybe handle gracefully
map.run_schedule().expect("Failed to run schedule!");
if let Some(max_frames) = max_frames {
if current_frame >= max_frames {
@ -193,8 +178,10 @@ pub struct WinitEventLoopProxy<ET: 'static> {
}
impl<ET: 'static> EventLoopProxy<ET> for WinitEventLoopProxy<ET> {
fn send_event(&self, event: ET) -> Result<(), Error> {
self.proxy.send_event(event).map_err(|e| Error::EventLoop)
fn send_event(&self, event: ET) -> Result<(), SendEventError> {
self.proxy
.send_event(event)
.map_err(|_e| SendEventError::Closed)
}
}

View File

@ -11,6 +11,7 @@ use maplibre::{
kernel::{Kernel, KernelBuilder},
map::Map,
platform::{http_client::ReqwestHttpClient, run_multithreaded, scheduler::TokioScheduler},
render::{builder::RendererBuilder, settings::WgpuSettings},
style::Style,
window::{MapWindow, MapWindowConfig, WindowSize},
};
@ -82,18 +83,16 @@ pub fn run_headed_map(cache_path: Option<String>) {
.with_scheduler(TokioScheduler::new())
.build();
let mut map = Map::new(Style::default(), kernel).unwrap();
let renderer_builder = RendererBuilder::new().with_wgpu_settings(WgpuSettings {
backends: Some(maplibre::render::settings::Backends::all()),
..WgpuSettings::default()
});
let mut map = Map::new(Style::default(), kernel, renderer_builder).unwrap();
#[cfg(not(target_os = "android"))]
{
use maplibre::render::{builder::RendererBuilder, settings::WgpuSettings};
map.initialize_renderer(RendererBuilder::new().with_wgpu_settings(WgpuSettings {
backends: Some(maplibre::render::settings::Backends::VULKAN), // FIXME: Change
..WgpuSettings::default()
}))
.await
.unwrap();
map.initialize_renderer().await.unwrap();
}
map.window_mut()

View File

@ -66,6 +66,7 @@ log = "0.4.17"
# Utils
bytemuck = "1.12.1"
bytemuck_derive = "1.2.1"
thiserror = "1.0"
# Static tiles inclusion
include_dir = "0.7.2"
@ -79,7 +80,6 @@ csscolorparser = { version = "0.6.2", features = ["serde", "cint"] }
cint = "0.3.1"
# Required by bevy renderer
thiserror = "1.0.32"
downcast-rs = "1.2.0"
smallvec = "1.9.0"

View File

@ -127,9 +127,9 @@ impl From<u8> for ZoomLevel {
}
}
impl Into<u8> for ZoomLevel {
fn into(self) -> u8 {
self.0
impl From<ZoomLevel> for u8 {
fn from(val: ZoomLevel) -> Self {
val.0
}
}

View File

@ -1,40 +0,0 @@
//! Errors which can happen in various parts of the library.
use std::{borrow::Cow, sync::mpsc::SendError};
use lyon::tessellation::TessellationError;
use crate::render::error::RenderError;
/// Enumeration of errors which can happen during the operation of the library.
#[derive(Debug)]
pub enum Error {
APC,
Scheduler,
EventLoop,
Network(String),
Tesselation(TessellationError),
Render(RenderError),
Generic(Cow<'static, str>),
}
impl<E> From<E> for Error
where
E: Into<RenderError>,
{
fn from(e: E) -> Self {
Error::Render(e.into())
}
}
impl From<TessellationError> for Error {
fn from(e: TessellationError) -> Self {
Error::Tesselation(e)
}
}
impl<T> From<SendError<T>> for Error {
fn from(_e: SendError<T>) -> Self {
Error::Scheduler
}
}

View File

@ -1,6 +1,7 @@
use thiserror::Error;
use crate::{
environment::Environment,
error::Error,
map::Map,
window::{HeadedMapWindow, MapWindowConfig},
};
@ -12,8 +13,16 @@ pub trait EventLoopConfig {
fn create_proxy() -> Self::EventLoopProxy;
}
/// When sending events to an event loop errors can occur.
#[derive(Error, Debug)]
pub enum SendEventError {
/// The event loop was already closed
#[error("event loop is closed")]
Closed,
}
pub trait EventLoopProxy<T: 'static> {
fn send_event(&self, event: T) -> Result<(), Error>;
fn send_event(&self, event: T) -> Result<(), SendEventError>;
}
pub trait EventLoop<ET: 'static + PartialEq> {

View File

@ -3,18 +3,19 @@ use std::collections::HashSet;
use crate::{
context::MapContext,
coords::{WorldCoords, WorldTileCoords, Zoom, TILE_SIZE},
error::Error,
headless::{
environment::HeadlessEnvironment, graph_node::CopySurfaceBufferNode,
stage::WriteSurfaceBufferStage,
},
io::{
pipeline::{PipelineContext, PipelineProcessor, Processable},
pipeline::{PipelineContext, PipelineError, PipelineProcessor, Processable},
source_client::SourceFetchError,
tile_pipelines::build_vector_tile_pipeline,
tile_repository::{StoredLayer, StoredTile},
RawLayer, TileRequest,
},
kernel::Kernel,
map::MapError,
render::{
create_default_render_graph, draw_graph, eventually::Eventually,
register_default_render_stages, stages::RenderStageLabel, Renderer, ShaderVertex,
@ -37,7 +38,7 @@ impl HeadlessMap {
renderer: Renderer,
kernel: Kernel<HeadlessEnvironment>,
write_to_disk: bool,
) -> Result<Self, Error> {
) -> Result<Self, MapError> {
let window_size = renderer.state().surface().size();
let world = World::new(
@ -47,7 +48,7 @@ impl HeadlessMap {
cgmath::Deg(0.0),
);
let mut graph = create_default_render_graph()?;
let mut graph = create_default_render_graph().map_err(|e| MapError::RenderGraphInit(e))?;
let draw_graph = graph
.get_sub_graph_mut(draw_graph::NAME)
.expect("Subgraph does not exist");
@ -74,7 +75,7 @@ impl HeadlessMap {
})
}
pub fn render_tile(&mut self, tile: StoredTile) -> Result<(), Error> {
pub fn render_tile(&mut self, tile: StoredTile) {
let context = &mut self.map_context;
if let Eventually::Initialized(pool) = context.renderer.state.buffer_pool_mut() {
@ -88,18 +89,19 @@ impl HeadlessMap {
context.world.tile_repository.put_tile(tile);
self.schedule.run(&mut self.map_context);
Ok(())
}
pub async fn fetch_tile(
&self,
coords: WorldTileCoords,
source_layers: &[&str],
) -> Result<StoredTile, Error> {
pub async fn fetch_tile(&self, coords: WorldTileCoords) -> Result<Box<[u8]>, SourceFetchError> {
let source_client = self.kernel.source_client();
let data = source_client.fetch(&coords).await?.into_boxed_slice();
Ok(source_client.fetch(&coords).await?.into_boxed_slice())
}
pub async fn process_tile(
&self,
tile_data: Box<[u8]>,
source_layers: &[&str],
) -> Result<StoredTile, PipelineError> {
let mut pipeline_context = PipelineContext::new(HeadlessPipelineProcessor::default());
let pipeline = build_vector_tile_pipeline();
@ -113,7 +115,7 @@ impl HeadlessMap {
.map(|layer| layer.to_string())
.collect::<HashSet<String>>(),
},
data,
tile_data,
),
&mut pipeline_context,
)?;
@ -138,7 +140,7 @@ impl PipelineProcessor for HeadlessPipelineProcessor {
buffer: OverAlignedVertexBuffer<ShaderVertex, IndexDataType>,
feature_indices: Vec<u32>,
layer_data: RawLayer,
) -> Result<(), Error> {
) -> Result<(), PipelineError> {
self.layers.push(StoredLayer::TessellatedLayer {
coords: *coords,
layer_name: layer_data.name,

View File

@ -1,7 +1,5 @@
use std::sync::Arc;
use tokio::{runtime::Handle, task};
use crate::{
context::MapContext,
render::{
@ -44,19 +42,21 @@ impl Stage for WriteSurfaceBufferStage {
let device = device.clone();
let current_frame = self.frame;
let buffer_slice = buffered_texture.map_async(&device);
let padded_buffer = buffer_slice.get_mapped_range();
if self.write_to_disk {
task::block_in_place(|| {
Handle::current().block_on(async {
buffered_texture
.create_png(
&device,
format!("frame_{}.png", current_frame).as_str(),
)
.await;
})
});
buffered_texture.write_png(
&padded_buffer,
format!("frame_{}.png", current_frame).as_str(),
);
}
// With the current interface, we have to make sure all mapped views are
// dropped before we unmap the buffer.
drop(padded_buffer);
buffered_texture.unmap();
self.frame += 1;
}
}

View File

@ -8,15 +8,13 @@ use std::{
};
use serde::{Deserialize, Serialize};
use thiserror::Error;
use crate::{
error::Error,
io::{
scheduler::Scheduler,
source_client::{HttpClient, HttpSourceClient, SourceClient},
transferables::{DefaultTransferables, Transferables},
TileRequest,
},
use crate::io::{
scheduler::Scheduler,
source_client::{HttpClient, HttpSourceClient, SourceClient},
transferables::{DefaultTransferables, Transferables},
TileRequest,
};
/// The result of the tessellation of a tile. This is sent as a message from a worker to the caller
@ -41,18 +39,47 @@ pub enum Input {
NotYetImplemented, // TODO: Placeholder, should be removed when second input is added
}
#[derive(Error, Debug)]
pub enum SendError {
#[error("could not transmit data")]
Transmission,
}
/// Allows sending messages from workers to back to the caller.
pub trait Context<T: Transferables, HC: HttpClient>: Send + 'static {
/// Send a message back to the caller.
fn send(&self, data: Message<T>) -> Result<(), Error>;
fn send(&self, data: Message<T>) -> Result<(), SendError>;
fn source_client(&self) -> &SourceClient<HC>;
}
#[derive(Error, Debug)]
pub enum ProcedureError {
/// The [`Input`] is not compatible with the procedure
#[error("provided input is not compatible with procedure")]
IncompatibleInput,
#[error("execution of procedure failed")]
Execution(Box<dyn std::error::Error>),
#[error("sending data failed")]
Send(SendError),
}
#[cfg(feature = "thread-safe-futures")]
pub type AsyncProcedureFuture = Pin<Box<(dyn Future<Output = Result<(), Error>> + Send + 'static)>>;
pub type AsyncProcedureFuture =
Pin<Box<(dyn Future<Output = Result<(), ProcedureError>> + Send + 'static)>>;
#[cfg(not(feature = "thread-safe-futures"))]
pub type AsyncProcedureFuture = Pin<Box<(dyn Future<Output = Result<(), Error>> + 'static)>>;
pub type AsyncProcedureFuture =
Pin<Box<(dyn Future<Output = Result<(), ProcedureError>> + 'static)>>;
#[derive(Error, Debug)]
pub enum CallError {
#[error("scheduling work failed")]
Schedule,
#[error("serializing data failed")]
Serialize(Box<dyn std::error::Error>),
#[error("deserializing failed")]
Deserialize(Box<dyn std::error::Error>),
}
/// Type definitions for asynchronous procedure calls. These functions can be called in an
/// [`AsyncProcedureCall`]. Functions of this type are required to be statically available at
@ -109,7 +136,8 @@ pub trait AsyncProcedureCall<HC: HttpClient>: 'static {
/// Call an [`AsyncProcedure`] using some [`Input`]. This function is non-blocking and
/// returns immediately.
fn call(&self, input: Input, procedure: AsyncProcedure<Self::Context>);
fn call(&self, input: Input, procedure: AsyncProcedure<Self::Context>)
-> Result<(), CallError>;
}
#[derive(Clone)]
@ -119,8 +147,8 @@ pub struct SchedulerContext<T: Transferables, HC: HttpClient> {
}
impl<T: Transferables, HC: HttpClient> Context<T, HC> for SchedulerContext<T, HC> {
fn send(&self, data: Message<T>) -> Result<(), Error> {
self.sender.send(data).map_err(|_e| Error::APC)
fn send(&self, data: Message<T>) -> Result<(), SendError> {
self.sender.send(data).map_err(|_e| SendError::Transmission)
}
fn source_client(&self) -> &SourceClient<HC> {
@ -156,13 +184,17 @@ impl<HC: HttpClient, S: Scheduler> AsyncProcedureCall<HC> for SchedulerAsyncProc
Some(transferred)
}
fn call(&self, input: Input, procedure: AsyncProcedure<Self::Context>) {
fn call(
&self,
input: Input,
procedure: AsyncProcedure<Self::Context>,
) -> Result<(), CallError> {
let sender = self.channel.0.clone();
let client = self.http_client.clone(); // TODO (perf): do not clone each time
self.scheduler
.schedule(move || async move {
(procedure)(
procedure(
input,
SchedulerContext {
sender,
@ -172,6 +204,6 @@ impl<HC: HttpClient, S: Scheduler> AsyncProcedureCall<HC> for SchedulerAsyncProc
.await
.unwrap();
})
.unwrap();
.map_err(|_e| CallError::Schedule)
}
}

View File

@ -2,25 +2,35 @@ use std::marker::PhantomData;
use downcast_rs::Downcast;
use geozero::mvt::tile;
use thiserror::Error;
use crate::{
coords::WorldTileCoords,
error::Error,
io::geometry_index::IndexedGeometry,
io::{apc::SendError, geometry_index::IndexedGeometry},
render::ShaderVertex,
tessellation::{IndexDataType, OverAlignedVertexBuffer},
};
#[derive(Error, Debug)]
pub enum PipelineError {
/// Sending of results failed
#[error("sending data back from pipeline failed")]
SendError(SendError),
/// Error during processing of the pipeline
#[error("processing data in pipeline failed")]
Processing(Box<dyn std::error::Error>),
}
/// Processes events which happen during the pipeline execution
pub trait PipelineProcessor: Downcast {
fn tile_finished(&mut self, _coords: &WorldTileCoords) -> Result<(), Error> {
fn tile_finished(&mut self, _coords: &WorldTileCoords) -> Result<(), PipelineError> {
Ok(())
}
fn layer_unavailable(
&mut self,
_coords: &WorldTileCoords,
_layer_name: &str,
) -> Result<(), Error> {
) -> Result<(), PipelineError> {
Ok(())
}
fn layer_tesselation_finished(
@ -29,14 +39,14 @@ pub trait PipelineProcessor: Downcast {
_buffer: OverAlignedVertexBuffer<ShaderVertex, IndexDataType>,
_feature_indices: Vec<u32>,
_layer_data: tile::Layer,
) -> Result<(), Error> {
) -> Result<(), PipelineError> {
Ok(())
}
fn layer_indexing_finished(
&mut self,
_coords: &WorldTileCoords,
_geometries: Vec<IndexedGeometry<f64>>,
) -> Result<(), Error> {
) -> Result<(), PipelineError> {
Ok(())
}
}
@ -76,7 +86,7 @@ pub trait Processable {
&self,
input: Self::Input,
context: &mut PipelineContext,
) -> Result<Self::Output, Error>;
) -> Result<Self::Output, PipelineError>;
}
/// A pipeline which consists of multiple steps. Steps are [`Processable`] workloads. Later steps
@ -112,7 +122,7 @@ where
&self,
input: Self::Input,
context: &mut PipelineContext,
) -> Result<Self::Output, Error> {
) -> Result<Self::Output, PipelineError> {
let output = self.step.process(input, context)?;
self.next_step.process(output, context)
}
@ -139,7 +149,7 @@ impl<I> Processable for PipelineEnd<I> {
&self,
input: Self::Input,
_context: &mut PipelineContext,
) -> Result<Self::Output, Error> {
) -> Result<Self::Output, PipelineError> {
Ok(input)
}
}
@ -152,7 +162,7 @@ impl<I, O> Processable for &fn(input: I, context: &mut PipelineContext) -> O {
&self,
input: Self::Input,
context: &mut PipelineContext,
) -> Result<Self::Output, Error> {
) -> Result<Self::Output, PipelineError> {
Ok((self)(input, context))
}
}
@ -165,7 +175,7 @@ impl<I, O> Processable for fn(input: I, context: &mut PipelineContext) -> O {
&self,
input: Self::Input,
context: &mut PipelineContext,
) -> Result<Self::Output, Error> {
) -> Result<Self::Output, PipelineError> {
Ok((self)(input, context))
}
}
@ -203,7 +213,7 @@ where
&self,
input: Self::Input,
context: &mut PipelineContext,
) -> Result<Self::Output, Error> {
) -> Result<Self::Output, PipelineError> {
Ok((self.func)(input, context))
}
}

View File

@ -2,7 +2,15 @@
use std::future::Future;
use crate::error::Error;
use thiserror::Error;
#[derive(Error, Debug)]
pub enum ScheduleError {
#[error("scheduling work failed")]
Scheduling(Box<dyn std::error::Error>),
#[error("scheduler is not implemented on this platform")]
NotImplemented,
}
/// Async/await scheduler.
/// Can schedule a task from a future factory and a shared state.
@ -11,7 +19,7 @@ pub trait Scheduler: 'static {
fn schedule<T>(
&self,
future_factory: impl (FnOnce() -> T) + Send + 'static,
) -> Result<(), Error>
) -> Result<(), ScheduleError>
where
T: Future<Output = ()> + Send + 'static;
@ -19,7 +27,7 @@ pub trait Scheduler: 'static {
fn schedule<T>(
&self,
future_factory: impl (FnOnce() -> T) + Send + 'static,
) -> Result<(), Error>
) -> Result<(), ScheduleError>
where
T: Future<Output = ()> + 'static;
}
@ -27,10 +35,13 @@ pub trait Scheduler: 'static {
pub struct NopScheduler;
impl Scheduler for NopScheduler {
fn schedule<T>(&self, _future_factory: impl FnOnce() -> T + Send + 'static) -> Result<(), Error>
fn schedule<T>(
&self,
_future_factory: impl FnOnce() -> T + Send + 'static,
) -> Result<(), ScheduleError>
where
T: Future<Output = ()> + 'static,
{
Err(Error::Scheduler)
Err(ScheduleError::NotImplemented)
}
}

View File

@ -1,8 +1,9 @@
//! HTTP client.
use async_trait::async_trait;
use thiserror::Error;
use crate::{coords::WorldTileCoords, error::Error, style::source::TileAddressingScheme};
use crate::{coords::WorldTileCoords, style::source::TileAddressingScheme};
/// A closure that returns a HTTP client.
pub type HTTPClientFactory<HC> = dyn Fn() -> HC;
@ -16,7 +17,7 @@ pub type HTTPClientFactory<HC> = dyn Fn() -> HC;
#[cfg_attr(not(feature = "thread-safe-futures"), async_trait(?Send))]
#[cfg_attr(feature = "thread-safe-futures", async_trait)]
pub trait HttpClient: Clone + Sync + Send + 'static {
async fn fetch(&self, url: &str) -> Result<Vec<u8>, Error>;
async fn fetch(&self, url: &str) -> Result<Vec<u8>, SourceFetchError>;
}
/// Gives access to the HTTP client which can be of multiple types,
@ -29,6 +30,10 @@ where
inner_client: HC,
}
#[derive(Error, Debug)]
#[error("failed to fetch from source")]
pub struct SourceFetchError(#[source] pub Box<dyn std::error::Error>);
/// Defines the different types of HTTP clients such as basic HTTP and Mbtiles.
/// More types might be coming such as S3 and other cloud http clients.
#[derive(Clone)]
@ -47,7 +52,7 @@ where
Self { http }
}
pub async fn fetch(&self, coords: &WorldTileCoords) -> Result<Vec<u8>, Error> {
pub async fn fetch(&self, coords: &WorldTileCoords) -> Result<Vec<u8>, SourceFetchError> {
self.http.fetch(coords).await
}
}
@ -62,7 +67,7 @@ where
}
}
pub async fn fetch(&self, coords: &WorldTileCoords) -> Result<Vec<u8>, Error> {
pub async fn fetch(&self, coords: &WorldTileCoords) -> Result<Vec<u8>, SourceFetchError> {
let tile_coords = coords.into_tile(TileAddressingScheme::TMS).unwrap();
self.inner_client
.fetch(

View File

@ -1,17 +1,32 @@
//! Static tile fetcher
use std::{concat, env};
use std::{
concat, env,
fmt::{Display, Formatter},
};
#[cfg(static_tiles_found)]
use include_dir::include_dir;
use include_dir::Dir;
use crate::{coords::TileCoords, error::Error};
use crate::coords::TileCoords;
#[cfg(static_tiles_found)]
static TILES: Dir = include_dir!("$OUT_DIR/extracted-tiles");
#[cfg(not(static_tiles_found))]
static TILES: Dir = Dir::new("/path", &[]);
#[derive(Debug)]
pub enum StaticFetchError {
/// Tile was not found in the static content
NotFound,
}
impl Display for StaticFetchError {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "{:?}", self)
}
}
/// Load PBF files which were statically embedded in the `build.rs`
#[derive(Default)]
pub struct StaticTileFetcher;
@ -25,15 +40,15 @@ impl StaticTileFetcher {
Self {}
}
/// Fetch the tile static file asynchrounously and returns a vector of bytes or a network error if the file
/// Fetch the tile static file asynchronously and returns a vector of bytes or a network error if the file
/// could not be fetched.
pub async fn fetch_tile(&self, coords: &TileCoords) -> Result<Vec<u8>, Error> {
pub async fn fetch_tile(&self, coords: &TileCoords) -> Result<Vec<u8>, StaticFetchError> {
self.sync_fetch_tile(coords)
}
/// Fetch the tile static file and returns a vector of bytes or a network error if the file
/// could not be fetched.
pub fn sync_fetch_tile(&self, coords: &TileCoords) -> Result<Vec<u8>, Error> {
pub fn sync_fetch_tile(&self, coords: &TileCoords) -> Result<Vec<u8>, StaticFetchError> {
if TILES.entries().is_empty() {
panic!(
"There are not tiles statically embedded in this binary! StaticTileFetcher will \
@ -43,9 +58,7 @@ impl StaticTileFetcher {
let tile = TILES
.get_file(format!("{}/{}/{}.{}", coords.z, coords.x, coords.y, "pbf"))
.ok_or_else(|| {
Error::Network("Failed to load tile from within the binary".to_string())
})?;
.ok_or_else(|| StaticFetchError::NotFound)?;
Ok(Vec::from(tile.contents()))
}
}

View File

@ -4,10 +4,9 @@ use geozero::GeozeroDatasource;
use prost::Message;
use crate::{
error::Error,
io::{
geometry_index::IndexProcessor,
pipeline::{DataPipeline, PipelineContext, PipelineEnd, Processable},
pipeline::{DataPipeline, PipelineContext, PipelineEnd, PipelineError, Processable},
TileRequest,
},
tessellation::{zero_tessellator::ZeroTessellator, IndexDataType},
@ -24,7 +23,7 @@ impl Processable for ParseTile {
&self,
(tile_request, data): Self::Input,
_context: &mut PipelineContext,
) -> Result<Self::Output, Error> {
) -> Result<Self::Output, PipelineError> {
let tile = geozero::mvt::Tile::decode(data.as_ref()).expect("failed to load tile");
Ok((tile_request, tile))
}
@ -41,7 +40,7 @@ impl Processable for IndexLayer {
&self,
(tile_request, mut tile): Self::Input,
context: &mut PipelineContext,
) -> Result<Self::Output, Error> {
) -> Result<Self::Output, PipelineError> {
let mut index = IndexProcessor::new();
for layer in &mut tile.layers {
@ -70,7 +69,7 @@ impl Processable for TessellateLayer {
&self,
(tile_request, mut tile): Self::Input,
context: &mut PipelineContext,
) -> Result<Self::Output, Error> {
) -> Result<Self::Output, PipelineError> {
let coords = &tile_request.coords;
for layer in &mut tile.layers {

View File

@ -23,7 +23,6 @@ pub(crate) mod tessellation;
pub mod context;
pub mod coords;
pub mod error;
#[cfg(feature = "headless")]
pub mod headless;
pub mod io;

View File

@ -1,16 +1,19 @@
use std::rc::Rc;
use thiserror::Error;
use crate::{
context::MapContext,
coords::{LatLon, Zoom},
environment::Environment,
error::Error,
kernel::Kernel,
render::{
builder::{
InitializationResult, InitializedRenderer, RendererBuilder, UninitializedRenderer,
},
create_default_render_graph, register_default_render_stages,
create_default_render_graph,
graph::RenderGraphError,
register_default_render_stages,
},
schedule::{Schedule, Stage},
stages::register_stages,
@ -19,9 +22,23 @@ use crate::{
world::World,
};
#[derive(Error, Debug)]
pub enum MapError {
/// No need to set renderer again
#[error("renderer was already set for this map")]
RendererAlreadySet,
#[error("initializing render graph failed")]
RenderGraphInit(RenderGraphError),
#[error("initializing device failed")]
DeviceInit,
}
pub enum MapContextState {
Ready(MapContext),
Pending { style: Style },
Pending {
style: Style,
renderer_builder: RendererBuilder,
},
}
pub struct Map<E: Environment> {
@ -35,7 +52,11 @@ impl<E: Environment> Map<E>
where
<<E as Environment>::MapWindowConfig as MapWindowConfig>::MapWindow: HeadedMapWindow,
{
pub fn new(style: Style, kernel: Kernel<E>) -> Result<Self, Error> {
pub fn new(
style: Style,
kernel: Kernel<E>,
renderer_builder: RendererBuilder,
) -> Result<Self, MapError> {
let mut schedule = Schedule::default();
let graph = create_default_render_graph().unwrap(); // TODO: Remove unwrap
@ -49,26 +70,30 @@ where
let map = Self {
kernel,
map_context: MapContextState::Pending { style },
map_context: MapContextState::Pending {
style,
renderer_builder,
},
schedule,
window,
};
Ok(map)
}
pub async fn initialize_renderer(
&mut self,
render_builder: RendererBuilder,
) -> Result<(), Error> {
let result = render_builder
.build()
.initialize_renderer::<E::MapWindowConfig>(&self.window)
.await
.expect("Failed to initialize renderer");
pub async fn initialize_renderer(&mut self) -> Result<(), MapError> {
match &mut self.map_context {
MapContextState::Ready(_) => Err(Error::Generic("Renderer is already set".into())),
MapContextState::Pending { style } => {
MapContextState::Ready(_) => Err(MapError::RendererAlreadySet),
MapContextState::Pending {
style,
renderer_builder,
} => {
let init_result = renderer_builder
.clone() // Cloning because we want to be able to build multiple times maybe
.build()
.initialize_renderer::<E::MapWindowConfig>(&self.window)
.await
.map_err(|e| MapError::DeviceInit)?;
let window_size = self.window.size();
let center = style.center.unwrap_or_default();
@ -80,7 +105,7 @@ where
cgmath::Deg::<f64>(style.pitch.unwrap_or_default()),
);
match result {
match init_result {
InitializationResult::Initialized(InitializedRenderer { renderer, .. }) => {
self.map_context = MapContextState::Ready(MapContext {
world,
@ -111,33 +136,27 @@ where
}
#[tracing::instrument(name = "update_and_redraw", skip_all)]
pub fn run_schedule(&mut self) -> Result<(), Error> {
pub fn run_schedule(&mut self) -> Result<(), MapError> {
match &mut self.map_context {
MapContextState::Ready(map_context) => {
self.schedule.run(map_context);
Ok(())
}
MapContextState::Pending { .. } => {
Err(Error::Generic("Renderer is already set".into()))
}
MapContextState::Pending { .. } => Err(MapError::RendererAlreadySet),
}
}
pub fn context(&self) -> Result<&MapContext, Error> {
pub fn context(&self) -> Result<&MapContext, MapError> {
match &self.map_context {
MapContextState::Ready(map_context) => Ok(map_context),
MapContextState::Pending { .. } => {
Err(Error::Generic("Renderer is already set".into()))
}
MapContextState::Pending { .. } => Err(MapError::RendererAlreadySet),
}
}
pub fn context_mut(&mut self) -> Result<&mut MapContext, Error> {
pub fn context_mut(&mut self) -> Result<&mut MapContext, MapError> {
match &mut self.map_context {
MapContextState::Ready(map_context) => Ok(map_context),
MapContextState::Pending { .. } => {
Err(Error::Generic("Renderer is already set".into()))
}
MapContextState::Pending { .. } => Err(MapError::RendererAlreadySet),
}
}

View File

@ -3,21 +3,22 @@ use reqwest::{Client, StatusCode};
use reqwest_middleware::ClientWithMiddleware;
use reqwest_middleware_cache::{managers::CACacheManager, Cache, CacheMode};
use crate::{error::Error, io::source_client::HttpClient};
use crate::io::source_client::{HttpClient, SourceFetchError};
#[derive(Clone)]
pub struct ReqwestHttpClient {
client: ClientWithMiddleware,
}
impl From<reqwest::Error> for Error {
impl From<reqwest::Error> for SourceFetchError {
fn from(err: reqwest::Error) -> Self {
Error::Network(err.to_string())
SourceFetchError(Box::new(err))
}
}
impl From<reqwest_middleware::Error> for Error {
impl From<reqwest_middleware::Error> for SourceFetchError {
fn from(err: reqwest_middleware::Error) -> Self {
Error::Network(err.to_string())
SourceFetchError(Box::new(err))
}
}
@ -43,7 +44,7 @@ impl ReqwestHttpClient {
#[cfg_attr(not(feature = "thread-safe-futures"), async_trait(?Send))]
#[cfg_attr(feature = "thread-safe-futures", async_trait)]
impl HttpClient for ReqwestHttpClient {
async fn fetch(&self, url: &str) -> Result<Vec<u8>, Error> {
async fn fetch(&self, url: &str) -> Result<Vec<u8>, SourceFetchError> {
let response = self.client.get(url).send().await?;
match response.error_for_status() {
Ok(response) => {
@ -54,7 +55,7 @@ impl HttpClient for ReqwestHttpClient {
let body = response.bytes().await?;
Ok(Vec::from(body.as_ref()))
}
Err(e) => Err(Error::Network(e.to_string())),
Err(e) => Err(SourceFetchError(Box::new(e))),
}
}
}

View File

@ -1,6 +1,6 @@
use std::future::Future;
use crate::{error::Error, io::scheduler::Scheduler};
use crate::io::scheduler::{ScheduleError, Scheduler};
/// Multi-threading with Tokio.
pub struct TokioScheduler;
@ -13,7 +13,10 @@ impl TokioScheduler {
impl Scheduler for TokioScheduler {
#[cfg(feature = "thread-safe-futures")]
fn schedule<T>(&self, future_factory: impl FnOnce() -> T + Send + 'static) -> Result<(), Error>
fn schedule<T>(
&self,
future_factory: impl FnOnce() -> T + Send + 'static,
) -> Result<(), ScheduleError>
where
T: Future<Output = ()> + Send + 'static,
{
@ -23,7 +26,10 @@ impl Scheduler for TokioScheduler {
// FIXME: Provide a working implementation
#[cfg(not(feature = "thread-safe-futures"))]
fn schedule<T>(&self, _future_factory: impl FnOnce() -> T + 'static) -> Result<(), Error>
fn schedule<T>(
&self,
_future_factory: impl FnOnce() -> T + 'static,
) -> Result<(), ScheduleError>
where
T: Future<Output = ()> + 'static,
{

View File

@ -1,12 +1,13 @@
use crate::{
error::Error,
render::{
error::RenderError,
settings::{RendererSettings, WgpuSettings},
Renderer,
},
window::{HeadedMapWindow, MapWindowConfig},
};
#[derive(Clone)]
pub struct RendererBuilder {
wgpu_settings: Option<WgpuSettings>,
renderer_settings: Option<RendererSettings>,
@ -87,7 +88,7 @@ impl UninitializedRenderer {
pub async fn initialize_renderer<MWC>(
self,
existing_window: &MWC::MapWindow,
) -> Result<InitializationResult, Error>
) -> Result<InitializationResult, RenderError>
where
MWC: MapWindowConfig,
<MWC as MapWindowConfig>::MapWindow: HeadedMapWindow,
@ -109,7 +110,7 @@ impl UninitializedRenderer {
pub(crate) async fn initialize_headless<MWC>(
self,
existing_window: &MWC::MapWindow,
) -> Result<Renderer, Error>
) -> Result<Renderer, RenderError>
where
MWC: MapWindowConfig,
{

View File

@ -1,6 +1,6 @@
use std::fmt;
use crate::{error::Error, render::graph::RenderGraphError};
use crate::render::graph::RenderGraphError;
#[derive(Debug)]
pub enum RenderError {
@ -37,14 +37,14 @@ impl From<RenderGraphError> for RenderError {
}
}
impl From<wgpu::SurfaceError> for Error {
impl From<wgpu::SurfaceError> for RenderError {
fn from(e: wgpu::SurfaceError) -> Self {
Error::Render(RenderError::Surface(e))
RenderError::Surface(e)
}
}
impl From<wgpu::RequestDeviceError> for Error {
impl From<wgpu::RequestDeviceError> for RenderError {
fn from(e: wgpu::RequestDeviceError) -> Self {
Error::Render(RenderError::Device(e))
RenderError::Device(e)
}
}

View File

@ -159,14 +159,14 @@ impl SlotInfos {
/// Retrieves the [`SlotInfo`] for the provided label.
pub fn get_slot(&self, label: impl Into<SlotLabel>) -> Option<&SlotInfo> {
let label = label.into();
let index = self.get_slot_index(&label)?;
let index = self.get_slot_index(label)?;
self.slots.get(index)
}
/// Retrieves the [`SlotInfo`] for the provided label mutably.
pub fn get_slot_mut(&mut self, label: impl Into<SlotLabel>) -> Option<&mut SlotInfo> {
let label = label.into();
let index = self.get_slot_index(&label)?;
let index = self.get_slot_index(label)?;
self.slots.get_mut(index)
}

View File

@ -20,8 +20,6 @@
use std::sync::Arc;
use log::info;
use crate::{
render::{
eventually::Eventually,
@ -246,10 +244,9 @@ impl Renderer {
let adapter = instance
.request_adapter(request_adapter_options)
.await
.expect("Unable to find a GPU! Make sure you have installed required drivers!");
.ok_or_else(|| wgpu::RequestDeviceError)?;
let adapter_info = adapter.get_info();
info!("{:?}", adapter_info);
#[cfg(not(target_arch = "wasm32"))]
let trace_path = if settings.record_trace {

View File

@ -67,13 +67,7 @@ pub struct BufferedTextureHead {
#[cfg(feature = "headless")]
impl BufferedTextureHead {
pub async fn create_png<'a>(
&self,
device: &wgpu::Device,
png_output_path: &str,
// device: &wgpu::Device,
) {
use std::{fs::File, io::Write};
pub fn map_async<'a>(&self, device: &wgpu::Device) -> wgpu::BufferSlice {
// Note that we're not calling `.await` here.
let buffer_slice = self.output_buffer.slice(..);
buffer_slice.map_async(wgpu::MapMode::Read, |_| ());
@ -82,8 +76,15 @@ impl BufferedTextureHead {
// In an actual application, `device.poll(...)` should
// be called in an event loop or on another thread.
device.poll(wgpu::Maintain::Wait);
let padded_buffer = buffer_slice.get_mapped_range();
buffer_slice
}
pub fn unmap(&self) {
self.output_buffer.unmap();
}
pub fn write_png<'a>(&self, padded_buffer: &wgpu::BufferView<'a>, png_output_path: &str) {
use std::{fs::File, io::Write};
let mut png_encoder = png::Encoder::new(
File::create(png_output_path).unwrap(), // TODO: Remove unwrap
self.buffer_dimensions.width as u32,
@ -104,12 +105,6 @@ impl BufferedTextureHead {
.unwrap(); // TODO: Remove unwrap
}
png_writer.finish().unwrap(); // TODO: Remove unwrap
// With the current interface, we have to make sure all mapped views are
// dropped before we unmap the buffer.
drop(padded_buffer);
self.output_buffer.unmap();
}
}

View File

@ -51,6 +51,8 @@ impl Stage for GraphRunnerStage {
}
}
// TODO: Replace panic with a graceful exit in the event loop
// if e.should_exit() { *control_flow = ControlFlow::Exit; }
panic!("Error running render graph: {:?}", e);
}

View File

@ -8,11 +8,10 @@ use request_stage::RequestStage;
use crate::{
coords::WorldTileCoords,
environment::Environment,
error::Error,
io::{
apc::{Context, Message},
geometry_index::{IndexedGeometry, TileIndex},
pipeline::PipelineProcessor,
pipeline::{PipelineError, PipelineProcessor},
source_client::HttpClient,
transferables::{
LayerIndexed, LayerTessellated, LayerUnavailable, TileTessellated, Transferables,
@ -43,23 +42,25 @@ pub struct HeadedPipelineProcessor<T: Transferables, HC: HttpClient, C: Context<
impl<'c, T: Transferables, HC: HttpClient, C: Context<T, HC>> PipelineProcessor
for HeadedPipelineProcessor<T, HC, C>
{
fn tile_finished(&mut self, coords: &WorldTileCoords) -> Result<(), Error> {
fn tile_finished(&mut self, coords: &WorldTileCoords) -> Result<(), PipelineError> {
self.context
.send(Message::TileTessellated(T::TileTessellated::build_from(
*coords,
)))
.map_err(|e| PipelineError::Processing(Box::new(e)))
}
fn layer_unavailable(
&mut self,
coords: &WorldTileCoords,
layer_name: &str,
) -> Result<(), Error> {
) -> Result<(), PipelineError> {
self.context
.send(Message::LayerUnavailable(T::LayerUnavailable::build_from(
*coords,
layer_name.to_owned(),
)))
.map_err(|e| PipelineError::Processing(Box::new(e)))
}
fn layer_tesselation_finished(
@ -68,7 +69,7 @@ impl<'c, T: Transferables, HC: HttpClient, C: Context<T, HC>> PipelineProcessor
buffer: OverAlignedVertexBuffer<ShaderVertex, IndexDataType>,
feature_indices: Vec<u32>,
layer_data: tile::Layer,
) -> Result<(), Error> {
) -> Result<(), PipelineError> {
self.context
.send(Message::LayerTessellated(T::LayerTessellated::build_from(
*coords,
@ -76,17 +77,19 @@ impl<'c, T: Transferables, HC: HttpClient, C: Context<T, HC>> PipelineProcessor
feature_indices,
layer_data,
)))
.map_err(|e| PipelineError::Processing(Box::new(e)))
}
fn layer_indexing_finished(
&mut self,
coords: &WorldTileCoords,
geometries: Vec<IndexedGeometry<f64>>,
) -> Result<(), Error> {
) -> Result<(), PipelineError> {
self.context
.send(Message::LayerIndexed(T::LayerIndexed::build_from(
*coords,
TileIndex::Linear { list: geometries },
)))
.map_err(|e| PipelineError::Processing(Box::new(e)))
}
}

View File

@ -6,9 +6,8 @@ use crate::{
context::MapContext,
coords::{ViewRegion, WorldTileCoords},
environment::Environment,
error::Error,
io::{
apc::{AsyncProcedureCall, AsyncProcedureFuture, Context, Input, Message},
apc::{AsyncProcedureCall, AsyncProcedureFuture, Context, Input, Message, ProcedureError},
pipeline::{PipelineContext, Processable},
tile_pipelines::build_vector_tile_pipeline,
tile_repository::TileRepository,
@ -71,7 +70,7 @@ pub fn schedule<
) -> AsyncProcedureFuture {
Box::pin(async move {
let Input::TileRequest(input) = input else {
return Err(Error::APC)
return Err(ProcedureError::IncompatibleInput)
};
let coords = input.coords;
@ -87,7 +86,9 @@ pub fn schedule<
phantom_hc: Default::default(),
});
let pipeline = build_vector_tile_pipeline();
pipeline.process((input, data), &mut pipeline_context)?;
pipeline
.process((input, data), &mut pipeline_context)
.map_err(|e| ProcedureError::Execution(Box::new(e)))?;
}
Err(e) => {
log::error!("{:?}", &e);
@ -100,7 +101,7 @@ pub fn schedule<
input.coords,
to_load.to_string(),
)),
)?;
).map_err(ProcedureError::Send)?;
}
}
}
@ -126,8 +127,7 @@ impl<E: Environment> RequestStage<E> {
for coords in view_region.iter() {
if coords.build_quad_key().is_some() {
// TODO: Make tesselation depend on style?
self.request_tile(tile_repository, coords, &source_layers)
.unwrap(); // TODO: Remove unwrap
self.request_tile(tile_repository, coords, &source_layers);
}
}
}
@ -137,7 +137,7 @@ impl<E: Environment> RequestStage<E> {
tile_repository: &mut TileRepository,
coords: WorldTileCoords,
layers: &HashSet<String>,
) -> Result<(), Error> {
) {
/* TODO: is this still required?
if !tile_repository.is_layers_missing(coords, layers) {
return Ok(false);
@ -147,20 +147,19 @@ impl<E: Environment> RequestStage<E> {
tile_repository.create_tile(coords);
tracing::info!("new tile request: {}", &coords);
self.kernel.apc().call(
Input::TileRequest(TileRequest {
coords,
layers: layers.clone(),
}),
schedule::<
E,
<E::AsyncProcedureCall as AsyncProcedureCall<
E::HttpClient,
>>::Context,
>,
);
self.kernel
.apc()
.call(
Input::TileRequest(TileRequest {
coords,
layers: layers.clone(),
}),
schedule::<
E,
<E::AsyncProcedureCall as AsyncProcedureCall<E::HttpClient>>::Context,
>,
)
.unwrap(); // TODO: Remove unwrap
}
Ok(())
}
}

View File

@ -1,13 +1,11 @@
//! Tessellation for lines and polygons is implemented here.
use std::ops::Add;
use bytemuck::Pod;
use lyon::tessellation::{
FillVertex, FillVertexConstructor, StrokeVertex, StrokeVertexConstructor, VertexBuffers,
};
use crate::{error::Error, render::ShaderVertex};
use crate::render::ShaderVertex;
pub mod zero_tessellator;
@ -16,14 +14,6 @@ const DEFAULT_TOLERANCE: f32 = 0.02;
/// Vertex buffers index data type.
pub type IndexDataType = u32; // Must match INDEX_FORMAT
/// An element that can be tessellated into vertex buffers.
pub trait Tessellated<I: Add> {
/// Returns a vertex buffer which represents some object like a layer. Each object can contain
/// multiple features. For each feature also the amount of indices is returned.
///
fn tessellate(&self) -> Result<(VertexBuffers<ShaderVertex, I>, Vec<u32>), Error>;
}
/// Constructor for Fill and Stroke vertices.
pub struct VertexConstructor {}

View File

@ -30,6 +30,8 @@ maplibre-winit = { path = "../maplibre-winit", version = "0.1.0" }
log = "0.4.17"
rand = { version = "0.7", features = ["wasm-bindgen"] }
thiserror = "1.0"
console_error_panic_hook = "0.1.7"
# Exact version requirement can be removed as soon as https://github.com/gfx-rs/wgpu/pull/2954 is merged
web-sys = { version = "0.3.58", features = [
@ -42,7 +44,7 @@ js-sys = "0.3.58"
wasm-bindgen = "0.2.81"
wasm-bindgen-futures = "0.4.31"
console_log = { version = "0.2.0", features = ["color"] }
tracing-wasm = { version = "0.2.1", optional = true } # TODO: Low quality dependency
tracing-wasm = { version = "0.2.1", optional = true } # TODO: Low quality dependency (remove in a separate PR!)
# For passing Inputs in AsyncProcedureCalls
serde_json = "1.0.85"
flatbuffers = "22.10.26"

View File

@ -71,9 +71,9 @@
}
},
"node_modules/@esbuild/android-arm": {
"version": "0.15.13",
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.15.13.tgz",
"integrity": "sha512-RY2fVI8O0iFUNvZirXaQ1vMvK0xhCcl0gqRj74Z6yEiO1zAUa7hbsdwZM1kzqbxHK7LFyMizipfXT3JME+12Hw==",
"version": "0.15.18",
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.15.18.tgz",
"integrity": "sha512-5GT+kcs2WVGjVs7+boataCkO5Fg0y4kCjzkB5bAip7H4jfnOS3dA6KPiww9W1OEKTKeAcUVhdZGvgI65OXmUnw==",
"cpu": [
"arm"
],
@ -87,9 +87,9 @@
}
},
"node_modules/@esbuild/linux-loong64": {
"version": "0.15.13",
"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.15.13.tgz",
"integrity": "sha512-+BoyIm4I8uJmH/QDIH0fu7MG0AEx9OXEDXnqptXCwKOlOqZiS4iraH1Nr7/ObLMokW3sOCeBNyD68ATcV9b9Ag==",
"version": "0.15.18",
"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.15.18.tgz",
"integrity": "sha512-L4jVKS82XVhw2nvzLg/19ClLWg0y27ulRwuP7lcyL6AbUWB5aPglXY3M21mauDQMDfRLs8cQmeT03r/+X3cZYQ==",
"cpu": [
"loong64"
],
@ -139,9 +139,9 @@
}
},
"node_modules/anymatch": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz",
"integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==",
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz",
"integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==",
"dev": true,
"dependencies": {
"normalize-path": "^3.0.0",
@ -265,9 +265,9 @@
"dev": true
},
"node_modules/esbuild": {
"version": "0.15.13",
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.15.13.tgz",
"integrity": "sha512-Cu3SC84oyzzhrK/YyN4iEVy2jZu5t2fz66HEOShHURcjSkOSAVL8C/gfUT+lDJxkVHpg8GZ10DD0rMHRPqMFaQ==",
"version": "0.15.18",
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.15.18.tgz",
"integrity": "sha512-x/R72SmW3sSFRm5zrrIjAhCeQSAWoni3CmHEqfQrZIQTM3lVCdehdwuIqaOtfC2slvpdlLa62GYoN8SxT23m6Q==",
"dev": true,
"hasInstallScript": true,
"bin": {
@ -277,34 +277,34 @@
"node": ">=12"
},
"optionalDependencies": {
"@esbuild/android-arm": "0.15.13",
"@esbuild/linux-loong64": "0.15.13",
"esbuild-android-64": "0.15.13",
"esbuild-android-arm64": "0.15.13",
"esbuild-darwin-64": "0.15.13",
"esbuild-darwin-arm64": "0.15.13",
"esbuild-freebsd-64": "0.15.13",
"esbuild-freebsd-arm64": "0.15.13",
"esbuild-linux-32": "0.15.13",
"esbuild-linux-64": "0.15.13",
"esbuild-linux-arm": "0.15.13",
"esbuild-linux-arm64": "0.15.13",
"esbuild-linux-mips64le": "0.15.13",
"esbuild-linux-ppc64le": "0.15.13",
"esbuild-linux-riscv64": "0.15.13",
"esbuild-linux-s390x": "0.15.13",
"esbuild-netbsd-64": "0.15.13",
"esbuild-openbsd-64": "0.15.13",
"esbuild-sunos-64": "0.15.13",
"esbuild-windows-32": "0.15.13",
"esbuild-windows-64": "0.15.13",
"esbuild-windows-arm64": "0.15.13"
"@esbuild/android-arm": "0.15.18",
"@esbuild/linux-loong64": "0.15.18",
"esbuild-android-64": "0.15.18",
"esbuild-android-arm64": "0.15.18",
"esbuild-darwin-64": "0.15.18",
"esbuild-darwin-arm64": "0.15.18",
"esbuild-freebsd-64": "0.15.18",
"esbuild-freebsd-arm64": "0.15.18",
"esbuild-linux-32": "0.15.18",
"esbuild-linux-64": "0.15.18",
"esbuild-linux-arm": "0.15.18",
"esbuild-linux-arm64": "0.15.18",
"esbuild-linux-mips64le": "0.15.18",
"esbuild-linux-ppc64le": "0.15.18",
"esbuild-linux-riscv64": "0.15.18",
"esbuild-linux-s390x": "0.15.18",
"esbuild-netbsd-64": "0.15.18",
"esbuild-openbsd-64": "0.15.18",
"esbuild-sunos-64": "0.15.18",
"esbuild-windows-32": "0.15.18",
"esbuild-windows-64": "0.15.18",
"esbuild-windows-arm64": "0.15.18"
}
},
"node_modules/esbuild-android-64": {
"version": "0.15.13",
"resolved": "https://registry.npmjs.org/esbuild-android-64/-/esbuild-android-64-0.15.13.tgz",
"integrity": "sha512-yRorukXBlokwTip+Sy4MYskLhJsO0Kn0/Fj43s1krVblfwP+hMD37a4Wmg139GEsMLl+vh8WXp2mq/cTA9J97g==",
"version": "0.15.18",
"resolved": "https://registry.npmjs.org/esbuild-android-64/-/esbuild-android-64-0.15.18.tgz",
"integrity": "sha512-wnpt3OXRhcjfIDSZu9bnzT4/TNTDsOUvip0foZOUBG7QbSt//w3QV4FInVJxNhKc/ErhUxc5z4QjHtMi7/TbgA==",
"cpu": [
"x64"
],
@ -318,9 +318,9 @@
}
},
"node_modules/esbuild-android-arm64": {
"version": "0.15.13",
"resolved": "https://registry.npmjs.org/esbuild-android-arm64/-/esbuild-android-arm64-0.15.13.tgz",
"integrity": "sha512-TKzyymLD6PiVeyYa4c5wdPw87BeAiTXNtK6amWUcXZxkV51gOk5u5qzmDaYSwiWeecSNHamFsaFjLoi32QR5/w==",
"version": "0.15.18",
"resolved": "https://registry.npmjs.org/esbuild-android-arm64/-/esbuild-android-arm64-0.15.18.tgz",
"integrity": "sha512-G4xu89B8FCzav9XU8EjsXacCKSG2FT7wW9J6hOc18soEHJdtWu03L3TQDGf0geNxfLTtxENKBzMSq9LlbjS8OQ==",
"cpu": [
"arm64"
],
@ -334,9 +334,9 @@
}
},
"node_modules/esbuild-darwin-64": {
"version": "0.15.13",
"resolved": "https://registry.npmjs.org/esbuild-darwin-64/-/esbuild-darwin-64-0.15.13.tgz",
"integrity": "sha512-WAx7c2DaOS6CrRcoYCgXgkXDliLnFv3pQLV6GeW1YcGEZq2Gnl8s9Pg7ahValZkpOa0iE/ojRVQ87sbUhF1Cbg==",
"version": "0.15.18",
"resolved": "https://registry.npmjs.org/esbuild-darwin-64/-/esbuild-darwin-64-0.15.18.tgz",
"integrity": "sha512-2WAvs95uPnVJPuYKP0Eqx+Dl/jaYseZEUUT1sjg97TJa4oBtbAKnPnl3b5M9l51/nbx7+QAEtuummJZW0sBEmg==",
"cpu": [
"x64"
],
@ -350,9 +350,9 @@
}
},
"node_modules/esbuild-darwin-arm64": {
"version": "0.15.13",
"resolved": "https://registry.npmjs.org/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.15.13.tgz",
"integrity": "sha512-U6jFsPfSSxC3V1CLiQqwvDuj3GGrtQNB3P3nNC3+q99EKf94UGpsG9l4CQ83zBs1NHrk1rtCSYT0+KfK5LsD8A==",
"version": "0.15.18",
"resolved": "https://registry.npmjs.org/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.15.18.tgz",
"integrity": "sha512-tKPSxcTJ5OmNb1btVikATJ8NftlyNlc8BVNtyT/UAr62JFOhwHlnoPrhYWz09akBLHI9nElFVfWSTSRsrZiDUA==",
"cpu": [
"arm64"
],
@ -366,9 +366,9 @@
}
},
"node_modules/esbuild-freebsd-64": {
"version": "0.15.13",
"resolved": "https://registry.npmjs.org/esbuild-freebsd-64/-/esbuild-freebsd-64-0.15.13.tgz",
"integrity": "sha512-whItJgDiOXaDG/idy75qqevIpZjnReZkMGCgQaBWZuKHoElDJC1rh7MpoUgupMcdfOd+PgdEwNQW9DAE6i8wyA==",
"version": "0.15.18",
"resolved": "https://registry.npmjs.org/esbuild-freebsd-64/-/esbuild-freebsd-64-0.15.18.tgz",
"integrity": "sha512-TT3uBUxkteAjR1QbsmvSsjpKjOX6UkCstr8nMr+q7zi3NuZ1oIpa8U41Y8I8dJH2fJgdC3Dj3CXO5biLQpfdZA==",
"cpu": [
"x64"
],
@ -382,9 +382,9 @@
}
},
"node_modules/esbuild-freebsd-arm64": {
"version": "0.15.13",
"resolved": "https://registry.npmjs.org/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.15.13.tgz",
"integrity": "sha512-6pCSWt8mLUbPtygv7cufV0sZLeylaMwS5Fznj6Rsx9G2AJJsAjQ9ifA+0rQEIg7DwJmi9it+WjzNTEAzzdoM3Q==",
"version": "0.15.18",
"resolved": "https://registry.npmjs.org/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.15.18.tgz",
"integrity": "sha512-R/oVr+X3Tkh+S0+tL41wRMbdWtpWB8hEAMsOXDumSSa6qJR89U0S/PpLXrGF7Wk/JykfpWNokERUpCeHDl47wA==",
"cpu": [
"arm64"
],
@ -398,9 +398,9 @@
}
},
"node_modules/esbuild-linux-32": {
"version": "0.15.13",
"resolved": "https://registry.npmjs.org/esbuild-linux-32/-/esbuild-linux-32-0.15.13.tgz",
"integrity": "sha512-VbZdWOEdrJiYApm2kkxoTOgsoCO1krBZ3quHdYk3g3ivWaMwNIVPIfEE0f0XQQ0u5pJtBsnk2/7OPiCFIPOe/w==",
"version": "0.15.18",
"resolved": "https://registry.npmjs.org/esbuild-linux-32/-/esbuild-linux-32-0.15.18.tgz",
"integrity": "sha512-lphF3HiCSYtaa9p1DtXndiQEeQDKPl9eN/XNoBf2amEghugNuqXNZA/ZovthNE2aa4EN43WroO0B85xVSjYkbg==",
"cpu": [
"ia32"
],
@ -414,9 +414,9 @@
}
},
"node_modules/esbuild-linux-64": {
"version": "0.15.13",
"resolved": "https://registry.npmjs.org/esbuild-linux-64/-/esbuild-linux-64-0.15.13.tgz",
"integrity": "sha512-rXmnArVNio6yANSqDQlIO4WiP+Cv7+9EuAHNnag7rByAqFVuRusLbGi2697A5dFPNXoO//IiogVwi3AdcfPC6A==",
"version": "0.15.18",
"resolved": "https://registry.npmjs.org/esbuild-linux-64/-/esbuild-linux-64-0.15.18.tgz",
"integrity": "sha512-hNSeP97IviD7oxLKFuii5sDPJ+QHeiFTFLoLm7NZQligur8poNOWGIgpQ7Qf8Balb69hptMZzyOBIPtY09GZYw==",
"cpu": [
"x64"
],
@ -430,9 +430,9 @@
}
},
"node_modules/esbuild-linux-arm": {
"version": "0.15.13",
"resolved": "https://registry.npmjs.org/esbuild-linux-arm/-/esbuild-linux-arm-0.15.13.tgz",
"integrity": "sha512-Ac6LpfmJO8WhCMQmO253xX2IU2B3wPDbl4IvR0hnqcPrdfCaUa2j/lLMGTjmQ4W5JsJIdHEdW12dG8lFS0MbxQ==",
"version": "0.15.18",
"resolved": "https://registry.npmjs.org/esbuild-linux-arm/-/esbuild-linux-arm-0.15.18.tgz",
"integrity": "sha512-UH779gstRblS4aoS2qpMl3wjg7U0j+ygu3GjIeTonCcN79ZvpPee12Qun3vcdxX+37O5LFxz39XeW2I9bybMVA==",
"cpu": [
"arm"
],
@ -446,9 +446,9 @@
}
},
"node_modules/esbuild-linux-arm64": {
"version": "0.15.13",
"resolved": "https://registry.npmjs.org/esbuild-linux-arm64/-/esbuild-linux-arm64-0.15.13.tgz",
"integrity": "sha512-alEMGU4Z+d17U7KQQw2IV8tQycO6T+rOrgW8OS22Ua25x6kHxoG6Ngry6Aq6uranC+pNWNMB6aHFPh7aTQdORQ==",
"version": "0.15.18",
"resolved": "https://registry.npmjs.org/esbuild-linux-arm64/-/esbuild-linux-arm64-0.15.18.tgz",
"integrity": "sha512-54qr8kg/6ilcxd+0V3h9rjT4qmjc0CccMVWrjOEM/pEcUzt8X62HfBSeZfT2ECpM7104mk4yfQXkosY8Quptug==",
"cpu": [
"arm64"
],
@ -462,9 +462,9 @@
}
},
"node_modules/esbuild-linux-mips64le": {
"version": "0.15.13",
"resolved": "https://registry.npmjs.org/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.15.13.tgz",
"integrity": "sha512-47PgmyYEu+yN5rD/MbwS6DxP2FSGPo4Uxg5LwIdxTiyGC2XKwHhHyW7YYEDlSuXLQXEdTO7mYe8zQ74czP7W8A==",
"version": "0.15.18",
"resolved": "https://registry.npmjs.org/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.15.18.tgz",
"integrity": "sha512-Mk6Ppwzzz3YbMl/ZZL2P0q1tnYqh/trYZ1VfNP47C31yT0K8t9s7Z077QrDA/guU60tGNp2GOwCQnp+DYv7bxQ==",
"cpu": [
"mips64el"
],
@ -478,9 +478,9 @@
}
},
"node_modules/esbuild-linux-ppc64le": {
"version": "0.15.13",
"resolved": "https://registry.npmjs.org/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.15.13.tgz",
"integrity": "sha512-z6n28h2+PC1Ayle9DjKoBRcx/4cxHoOa2e689e2aDJSaKug3jXcQw7mM+GLg+9ydYoNzj8QxNL8ihOv/OnezhA==",
"version": "0.15.18",
"resolved": "https://registry.npmjs.org/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.15.18.tgz",
"integrity": "sha512-b0XkN4pL9WUulPTa/VKHx2wLCgvIAbgwABGnKMY19WhKZPT+8BxhZdqz6EgkqCLld7X5qiCY2F/bfpUUlnFZ9w==",
"cpu": [
"ppc64"
],
@ -494,9 +494,9 @@
}
},
"node_modules/esbuild-linux-riscv64": {
"version": "0.15.13",
"resolved": "https://registry.npmjs.org/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.15.13.tgz",
"integrity": "sha512-+Lu4zuuXuQhgLUGyZloWCqTslcCAjMZH1k3Xc9MSEJEpEFdpsSU0sRDXAnk18FKOfEjhu4YMGaykx9xjtpA6ow==",
"version": "0.15.18",
"resolved": "https://registry.npmjs.org/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.15.18.tgz",
"integrity": "sha512-ba2COaoF5wL6VLZWn04k+ACZjZ6NYniMSQStodFKH/Pu6RxzQqzsmjR1t9QC89VYJxBeyVPTaHuBMCejl3O/xg==",
"cpu": [
"riscv64"
],
@ -510,9 +510,9 @@
}
},
"node_modules/esbuild-linux-s390x": {
"version": "0.15.13",
"resolved": "https://registry.npmjs.org/esbuild-linux-s390x/-/esbuild-linux-s390x-0.15.13.tgz",
"integrity": "sha512-BMeXRljruf7J0TMxD5CIXS65y7puiZkAh+s4XFV9qy16SxOuMhxhVIXYLnbdfLrsYGFzx7U9mcdpFWkkvy/Uag==",
"version": "0.15.18",
"resolved": "https://registry.npmjs.org/esbuild-linux-s390x/-/esbuild-linux-s390x-0.15.18.tgz",
"integrity": "sha512-VbpGuXEl5FCs1wDVp93O8UIzl3ZrglgnSQ+Hu79g7hZu6te6/YHgVJxCM2SqfIila0J3k0csfnf8VD2W7u2kzQ==",
"cpu": [
"s390x"
],
@ -526,9 +526,9 @@
}
},
"node_modules/esbuild-netbsd-64": {
"version": "0.15.13",
"resolved": "https://registry.npmjs.org/esbuild-netbsd-64/-/esbuild-netbsd-64-0.15.13.tgz",
"integrity": "sha512-EHj9QZOTel581JPj7UO3xYbltFTYnHy+SIqJVq6yd3KkCrsHRbapiPb0Lx3EOOtybBEE9EyqbmfW1NlSDsSzvQ==",
"version": "0.15.18",
"resolved": "https://registry.npmjs.org/esbuild-netbsd-64/-/esbuild-netbsd-64-0.15.18.tgz",
"integrity": "sha512-98ukeCdvdX7wr1vUYQzKo4kQ0N2p27H7I11maINv73fVEXt2kyh4K4m9f35U1K43Xc2QGXlzAw0K9yoU7JUjOg==",
"cpu": [
"x64"
],
@ -542,9 +542,9 @@
}
},
"node_modules/esbuild-openbsd-64": {
"version": "0.15.13",
"resolved": "https://registry.npmjs.org/esbuild-openbsd-64/-/esbuild-openbsd-64-0.15.13.tgz",
"integrity": "sha512-nkuDlIjF/sfUhfx8SKq0+U+Fgx5K9JcPq1mUodnxI0x4kBdCv46rOGWbuJ6eof2n3wdoCLccOoJAbg9ba/bT2w==",
"version": "0.15.18",
"resolved": "https://registry.npmjs.org/esbuild-openbsd-64/-/esbuild-openbsd-64-0.15.18.tgz",
"integrity": "sha512-yK5NCcH31Uae076AyQAXeJzt/vxIo9+omZRKj1pauhk3ITuADzuOx5N2fdHrAKPxN+zH3w96uFKlY7yIn490xQ==",
"cpu": [
"x64"
],
@ -568,9 +568,9 @@
}
},
"node_modules/esbuild-sunos-64": {
"version": "0.15.13",
"resolved": "https://registry.npmjs.org/esbuild-sunos-64/-/esbuild-sunos-64-0.15.13.tgz",
"integrity": "sha512-jVeu2GfxZQ++6lRdY43CS0Tm/r4WuQQ0Pdsrxbw+aOrHQPHV0+LNOLnvbN28M7BSUGnJnHkHm2HozGgNGyeIRw==",
"version": "0.15.18",
"resolved": "https://registry.npmjs.org/esbuild-sunos-64/-/esbuild-sunos-64-0.15.18.tgz",
"integrity": "sha512-On22LLFlBeLNj/YF3FT+cXcyKPEI263nflYlAhz5crxtp3yRG1Ugfr7ITyxmCmjm4vbN/dGrb/B7w7U8yJR9yw==",
"cpu": [
"x64"
],
@ -584,9 +584,9 @@
}
},
"node_modules/esbuild-windows-32": {
"version": "0.15.13",
"resolved": "https://registry.npmjs.org/esbuild-windows-32/-/esbuild-windows-32-0.15.13.tgz",
"integrity": "sha512-XoF2iBf0wnqo16SDq+aDGi/+QbaLFpkiRarPVssMh9KYbFNCqPLlGAWwDvxEVz+ywX6Si37J2AKm+AXq1kC0JA==",
"version": "0.15.18",
"resolved": "https://registry.npmjs.org/esbuild-windows-32/-/esbuild-windows-32-0.15.18.tgz",
"integrity": "sha512-o+eyLu2MjVny/nt+E0uPnBxYuJHBvho8vWsC2lV61A7wwTWC3jkN2w36jtA+yv1UgYkHRihPuQsL23hsCYGcOQ==",
"cpu": [
"ia32"
],
@ -600,9 +600,9 @@
}
},
"node_modules/esbuild-windows-64": {
"version": "0.15.13",
"resolved": "https://registry.npmjs.org/esbuild-windows-64/-/esbuild-windows-64-0.15.13.tgz",
"integrity": "sha512-Et6htEfGycjDrtqb2ng6nT+baesZPYQIW+HUEHK4D1ncggNrDNk3yoboYQ5KtiVrw/JaDMNttz8rrPubV/fvPQ==",
"version": "0.15.18",
"resolved": "https://registry.npmjs.org/esbuild-windows-64/-/esbuild-windows-64-0.15.18.tgz",
"integrity": "sha512-qinug1iTTaIIrCorAUjR0fcBk24fjzEedFYhhispP8Oc7SFvs+XeW3YpAKiKp8dRpizl4YYAhxMjlftAMJiaUw==",
"cpu": [
"x64"
],
@ -616,9 +616,9 @@
}
},
"node_modules/esbuild-windows-arm64": {
"version": "0.15.13",
"resolved": "https://registry.npmjs.org/esbuild-windows-arm64/-/esbuild-windows-arm64-0.15.13.tgz",
"integrity": "sha512-3bv7tqntThQC9SWLRouMDmZnlOukBhOCTlkzNqzGCmrkCJI7io5LLjwJBOVY6kOUlIvdxbooNZwjtBvj+7uuVg==",
"version": "0.15.18",
"resolved": "https://registry.npmjs.org/esbuild-windows-arm64/-/esbuild-windows-arm64-0.15.18.tgz",
"integrity": "sha512-q9bsYzegpZcLziq0zgUi5KqGVtfhjxGbnksaBFYmWLxeV/S1fK4OLdq2DFYnXcLMjlZw2L0jLsk1eGoB522WXQ==",
"cpu": [
"arm64"
],
@ -947,9 +947,9 @@
}
},
"node_modules/typescript": {
"version": "4.8.4",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.8.4.tgz",
"integrity": "sha512-QCh+85mCy+h0IGff8r5XWzOVSbBO+KfeYrMQh7NJ58QujwcE22u+NUSmUxqF+un70P9GXKxa2HCNiTTMJknyjQ==",
"version": "4.9.4",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.4.tgz",
"integrity": "sha512-Uz+dTXYzxXXbsFpM86Wh3dKCxrQqUcVMxwU54orwlJjOpO3ao8L7j5lH+dWfTwgCwIuM9GQ2kvVotzYJMXTBZg==",
"dev": true,
"bin": {
"tsc": "bin/tsc",
@ -1056,16 +1056,16 @@
"dev": true
},
"@esbuild/android-arm": {
"version": "0.15.13",
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.15.13.tgz",
"integrity": "sha512-RY2fVI8O0iFUNvZirXaQ1vMvK0xhCcl0gqRj74Z6yEiO1zAUa7hbsdwZM1kzqbxHK7LFyMizipfXT3JME+12Hw==",
"version": "0.15.18",
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.15.18.tgz",
"integrity": "sha512-5GT+kcs2WVGjVs7+boataCkO5Fg0y4kCjzkB5bAip7H4jfnOS3dA6KPiww9W1OEKTKeAcUVhdZGvgI65OXmUnw==",
"dev": true,
"optional": true
},
"@esbuild/linux-loong64": {
"version": "0.15.13",
"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.15.13.tgz",
"integrity": "sha512-+BoyIm4I8uJmH/QDIH0fu7MG0AEx9OXEDXnqptXCwKOlOqZiS4iraH1Nr7/ObLMokW3sOCeBNyD68ATcV9b9Ag==",
"version": "0.15.18",
"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.15.18.tgz",
"integrity": "sha512-L4jVKS82XVhw2nvzLg/19ClLWg0y27ulRwuP7lcyL6AbUWB5aPglXY3M21mauDQMDfRLs8cQmeT03r/+X3cZYQ==",
"dev": true,
"optional": true
},
@ -1094,9 +1094,9 @@
}
},
"anymatch": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz",
"integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==",
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz",
"integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==",
"dev": true,
"requires": {
"normalize-path": "^3.0.0",
@ -1184,144 +1184,144 @@
"dev": true
},
"esbuild": {
"version": "0.15.13",
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.15.13.tgz",
"integrity": "sha512-Cu3SC84oyzzhrK/YyN4iEVy2jZu5t2fz66HEOShHURcjSkOSAVL8C/gfUT+lDJxkVHpg8GZ10DD0rMHRPqMFaQ==",
"version": "0.15.18",
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.15.18.tgz",
"integrity": "sha512-x/R72SmW3sSFRm5zrrIjAhCeQSAWoni3CmHEqfQrZIQTM3lVCdehdwuIqaOtfC2slvpdlLa62GYoN8SxT23m6Q==",
"dev": true,
"requires": {
"@esbuild/android-arm": "0.15.13",
"@esbuild/linux-loong64": "0.15.13",
"esbuild-android-64": "0.15.13",
"esbuild-android-arm64": "0.15.13",
"esbuild-darwin-64": "0.15.13",
"esbuild-darwin-arm64": "0.15.13",
"esbuild-freebsd-64": "0.15.13",
"esbuild-freebsd-arm64": "0.15.13",
"esbuild-linux-32": "0.15.13",
"esbuild-linux-64": "0.15.13",
"esbuild-linux-arm": "0.15.13",
"esbuild-linux-arm64": "0.15.13",
"esbuild-linux-mips64le": "0.15.13",
"esbuild-linux-ppc64le": "0.15.13",
"esbuild-linux-riscv64": "0.15.13",
"esbuild-linux-s390x": "0.15.13",
"esbuild-netbsd-64": "0.15.13",
"esbuild-openbsd-64": "0.15.13",
"esbuild-sunos-64": "0.15.13",
"esbuild-windows-32": "0.15.13",
"esbuild-windows-64": "0.15.13",
"esbuild-windows-arm64": "0.15.13"
"@esbuild/android-arm": "0.15.18",
"@esbuild/linux-loong64": "0.15.18",
"esbuild-android-64": "0.15.18",
"esbuild-android-arm64": "0.15.18",
"esbuild-darwin-64": "0.15.18",
"esbuild-darwin-arm64": "0.15.18",
"esbuild-freebsd-64": "0.15.18",
"esbuild-freebsd-arm64": "0.15.18",
"esbuild-linux-32": "0.15.18",
"esbuild-linux-64": "0.15.18",
"esbuild-linux-arm": "0.15.18",
"esbuild-linux-arm64": "0.15.18",
"esbuild-linux-mips64le": "0.15.18",
"esbuild-linux-ppc64le": "0.15.18",
"esbuild-linux-riscv64": "0.15.18",
"esbuild-linux-s390x": "0.15.18",
"esbuild-netbsd-64": "0.15.18",
"esbuild-openbsd-64": "0.15.18",
"esbuild-sunos-64": "0.15.18",
"esbuild-windows-32": "0.15.18",
"esbuild-windows-64": "0.15.18",
"esbuild-windows-arm64": "0.15.18"
}
},
"esbuild-android-64": {
"version": "0.15.13",
"resolved": "https://registry.npmjs.org/esbuild-android-64/-/esbuild-android-64-0.15.13.tgz",
"integrity": "sha512-yRorukXBlokwTip+Sy4MYskLhJsO0Kn0/Fj43s1krVblfwP+hMD37a4Wmg139GEsMLl+vh8WXp2mq/cTA9J97g==",
"version": "0.15.18",
"resolved": "https://registry.npmjs.org/esbuild-android-64/-/esbuild-android-64-0.15.18.tgz",
"integrity": "sha512-wnpt3OXRhcjfIDSZu9bnzT4/TNTDsOUvip0foZOUBG7QbSt//w3QV4FInVJxNhKc/ErhUxc5z4QjHtMi7/TbgA==",
"dev": true,
"optional": true
},
"esbuild-android-arm64": {
"version": "0.15.13",
"resolved": "https://registry.npmjs.org/esbuild-android-arm64/-/esbuild-android-arm64-0.15.13.tgz",
"integrity": "sha512-TKzyymLD6PiVeyYa4c5wdPw87BeAiTXNtK6amWUcXZxkV51gOk5u5qzmDaYSwiWeecSNHamFsaFjLoi32QR5/w==",
"version": "0.15.18",
"resolved": "https://registry.npmjs.org/esbuild-android-arm64/-/esbuild-android-arm64-0.15.18.tgz",
"integrity": "sha512-G4xu89B8FCzav9XU8EjsXacCKSG2FT7wW9J6hOc18soEHJdtWu03L3TQDGf0geNxfLTtxENKBzMSq9LlbjS8OQ==",
"dev": true,
"optional": true
},
"esbuild-darwin-64": {
"version": "0.15.13",
"resolved": "https://registry.npmjs.org/esbuild-darwin-64/-/esbuild-darwin-64-0.15.13.tgz",
"integrity": "sha512-WAx7c2DaOS6CrRcoYCgXgkXDliLnFv3pQLV6GeW1YcGEZq2Gnl8s9Pg7ahValZkpOa0iE/ojRVQ87sbUhF1Cbg==",
"version": "0.15.18",
"resolved": "https://registry.npmjs.org/esbuild-darwin-64/-/esbuild-darwin-64-0.15.18.tgz",
"integrity": "sha512-2WAvs95uPnVJPuYKP0Eqx+Dl/jaYseZEUUT1sjg97TJa4oBtbAKnPnl3b5M9l51/nbx7+QAEtuummJZW0sBEmg==",
"dev": true,
"optional": true
},
"esbuild-darwin-arm64": {
"version": "0.15.13",
"resolved": "https://registry.npmjs.org/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.15.13.tgz",
"integrity": "sha512-U6jFsPfSSxC3V1CLiQqwvDuj3GGrtQNB3P3nNC3+q99EKf94UGpsG9l4CQ83zBs1NHrk1rtCSYT0+KfK5LsD8A==",
"version": "0.15.18",
"resolved": "https://registry.npmjs.org/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.15.18.tgz",
"integrity": "sha512-tKPSxcTJ5OmNb1btVikATJ8NftlyNlc8BVNtyT/UAr62JFOhwHlnoPrhYWz09akBLHI9nElFVfWSTSRsrZiDUA==",
"dev": true,
"optional": true
},
"esbuild-freebsd-64": {
"version": "0.15.13",
"resolved": "https://registry.npmjs.org/esbuild-freebsd-64/-/esbuild-freebsd-64-0.15.13.tgz",
"integrity": "sha512-whItJgDiOXaDG/idy75qqevIpZjnReZkMGCgQaBWZuKHoElDJC1rh7MpoUgupMcdfOd+PgdEwNQW9DAE6i8wyA==",
"version": "0.15.18",
"resolved": "https://registry.npmjs.org/esbuild-freebsd-64/-/esbuild-freebsd-64-0.15.18.tgz",
"integrity": "sha512-TT3uBUxkteAjR1QbsmvSsjpKjOX6UkCstr8nMr+q7zi3NuZ1oIpa8U41Y8I8dJH2fJgdC3Dj3CXO5biLQpfdZA==",
"dev": true,
"optional": true
},
"esbuild-freebsd-arm64": {
"version": "0.15.13",
"resolved": "https://registry.npmjs.org/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.15.13.tgz",
"integrity": "sha512-6pCSWt8mLUbPtygv7cufV0sZLeylaMwS5Fznj6Rsx9G2AJJsAjQ9ifA+0rQEIg7DwJmi9it+WjzNTEAzzdoM3Q==",
"version": "0.15.18",
"resolved": "https://registry.npmjs.org/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.15.18.tgz",
"integrity": "sha512-R/oVr+X3Tkh+S0+tL41wRMbdWtpWB8hEAMsOXDumSSa6qJR89U0S/PpLXrGF7Wk/JykfpWNokERUpCeHDl47wA==",
"dev": true,
"optional": true
},
"esbuild-linux-32": {
"version": "0.15.13",
"resolved": "https://registry.npmjs.org/esbuild-linux-32/-/esbuild-linux-32-0.15.13.tgz",
"integrity": "sha512-VbZdWOEdrJiYApm2kkxoTOgsoCO1krBZ3quHdYk3g3ivWaMwNIVPIfEE0f0XQQ0u5pJtBsnk2/7OPiCFIPOe/w==",
"version": "0.15.18",
"resolved": "https://registry.npmjs.org/esbuild-linux-32/-/esbuild-linux-32-0.15.18.tgz",
"integrity": "sha512-lphF3HiCSYtaa9p1DtXndiQEeQDKPl9eN/XNoBf2amEghugNuqXNZA/ZovthNE2aa4EN43WroO0B85xVSjYkbg==",
"dev": true,
"optional": true
},
"esbuild-linux-64": {
"version": "0.15.13",
"resolved": "https://registry.npmjs.org/esbuild-linux-64/-/esbuild-linux-64-0.15.13.tgz",
"integrity": "sha512-rXmnArVNio6yANSqDQlIO4WiP+Cv7+9EuAHNnag7rByAqFVuRusLbGi2697A5dFPNXoO//IiogVwi3AdcfPC6A==",
"version": "0.15.18",
"resolved": "https://registry.npmjs.org/esbuild-linux-64/-/esbuild-linux-64-0.15.18.tgz",
"integrity": "sha512-hNSeP97IviD7oxLKFuii5sDPJ+QHeiFTFLoLm7NZQligur8poNOWGIgpQ7Qf8Balb69hptMZzyOBIPtY09GZYw==",
"dev": true,
"optional": true
},
"esbuild-linux-arm": {
"version": "0.15.13",
"resolved": "https://registry.npmjs.org/esbuild-linux-arm/-/esbuild-linux-arm-0.15.13.tgz",
"integrity": "sha512-Ac6LpfmJO8WhCMQmO253xX2IU2B3wPDbl4IvR0hnqcPrdfCaUa2j/lLMGTjmQ4W5JsJIdHEdW12dG8lFS0MbxQ==",
"version": "0.15.18",
"resolved": "https://registry.npmjs.org/esbuild-linux-arm/-/esbuild-linux-arm-0.15.18.tgz",
"integrity": "sha512-UH779gstRblS4aoS2qpMl3wjg7U0j+ygu3GjIeTonCcN79ZvpPee12Qun3vcdxX+37O5LFxz39XeW2I9bybMVA==",
"dev": true,
"optional": true
},
"esbuild-linux-arm64": {
"version": "0.15.13",
"resolved": "https://registry.npmjs.org/esbuild-linux-arm64/-/esbuild-linux-arm64-0.15.13.tgz",
"integrity": "sha512-alEMGU4Z+d17U7KQQw2IV8tQycO6T+rOrgW8OS22Ua25x6kHxoG6Ngry6Aq6uranC+pNWNMB6aHFPh7aTQdORQ==",
"version": "0.15.18",
"resolved": "https://registry.npmjs.org/esbuild-linux-arm64/-/esbuild-linux-arm64-0.15.18.tgz",
"integrity": "sha512-54qr8kg/6ilcxd+0V3h9rjT4qmjc0CccMVWrjOEM/pEcUzt8X62HfBSeZfT2ECpM7104mk4yfQXkosY8Quptug==",
"dev": true,
"optional": true
},
"esbuild-linux-mips64le": {
"version": "0.15.13",
"resolved": "https://registry.npmjs.org/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.15.13.tgz",
"integrity": "sha512-47PgmyYEu+yN5rD/MbwS6DxP2FSGPo4Uxg5LwIdxTiyGC2XKwHhHyW7YYEDlSuXLQXEdTO7mYe8zQ74czP7W8A==",
"version": "0.15.18",
"resolved": "https://registry.npmjs.org/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.15.18.tgz",
"integrity": "sha512-Mk6Ppwzzz3YbMl/ZZL2P0q1tnYqh/trYZ1VfNP47C31yT0K8t9s7Z077QrDA/guU60tGNp2GOwCQnp+DYv7bxQ==",
"dev": true,
"optional": true
},
"esbuild-linux-ppc64le": {
"version": "0.15.13",
"resolved": "https://registry.npmjs.org/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.15.13.tgz",
"integrity": "sha512-z6n28h2+PC1Ayle9DjKoBRcx/4cxHoOa2e689e2aDJSaKug3jXcQw7mM+GLg+9ydYoNzj8QxNL8ihOv/OnezhA==",
"version": "0.15.18",
"resolved": "https://registry.npmjs.org/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.15.18.tgz",
"integrity": "sha512-b0XkN4pL9WUulPTa/VKHx2wLCgvIAbgwABGnKMY19WhKZPT+8BxhZdqz6EgkqCLld7X5qiCY2F/bfpUUlnFZ9w==",
"dev": true,
"optional": true
},
"esbuild-linux-riscv64": {
"version": "0.15.13",
"resolved": "https://registry.npmjs.org/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.15.13.tgz",
"integrity": "sha512-+Lu4zuuXuQhgLUGyZloWCqTslcCAjMZH1k3Xc9MSEJEpEFdpsSU0sRDXAnk18FKOfEjhu4YMGaykx9xjtpA6ow==",
"version": "0.15.18",
"resolved": "https://registry.npmjs.org/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.15.18.tgz",
"integrity": "sha512-ba2COaoF5wL6VLZWn04k+ACZjZ6NYniMSQStodFKH/Pu6RxzQqzsmjR1t9QC89VYJxBeyVPTaHuBMCejl3O/xg==",
"dev": true,
"optional": true
},
"esbuild-linux-s390x": {
"version": "0.15.13",
"resolved": "https://registry.npmjs.org/esbuild-linux-s390x/-/esbuild-linux-s390x-0.15.13.tgz",
"integrity": "sha512-BMeXRljruf7J0TMxD5CIXS65y7puiZkAh+s4XFV9qy16SxOuMhxhVIXYLnbdfLrsYGFzx7U9mcdpFWkkvy/Uag==",
"version": "0.15.18",
"resolved": "https://registry.npmjs.org/esbuild-linux-s390x/-/esbuild-linux-s390x-0.15.18.tgz",
"integrity": "sha512-VbpGuXEl5FCs1wDVp93O8UIzl3ZrglgnSQ+Hu79g7hZu6te6/YHgVJxCM2SqfIila0J3k0csfnf8VD2W7u2kzQ==",
"dev": true,
"optional": true
},
"esbuild-netbsd-64": {
"version": "0.15.13",
"resolved": "https://registry.npmjs.org/esbuild-netbsd-64/-/esbuild-netbsd-64-0.15.13.tgz",
"integrity": "sha512-EHj9QZOTel581JPj7UO3xYbltFTYnHy+SIqJVq6yd3KkCrsHRbapiPb0Lx3EOOtybBEE9EyqbmfW1NlSDsSzvQ==",
"version": "0.15.18",
"resolved": "https://registry.npmjs.org/esbuild-netbsd-64/-/esbuild-netbsd-64-0.15.18.tgz",
"integrity": "sha512-98ukeCdvdX7wr1vUYQzKo4kQ0N2p27H7I11maINv73fVEXt2kyh4K4m9f35U1K43Xc2QGXlzAw0K9yoU7JUjOg==",
"dev": true,
"optional": true
},
"esbuild-openbsd-64": {
"version": "0.15.13",
"resolved": "https://registry.npmjs.org/esbuild-openbsd-64/-/esbuild-openbsd-64-0.15.13.tgz",
"integrity": "sha512-nkuDlIjF/sfUhfx8SKq0+U+Fgx5K9JcPq1mUodnxI0x4kBdCv46rOGWbuJ6eof2n3wdoCLccOoJAbg9ba/bT2w==",
"version": "0.15.18",
"resolved": "https://registry.npmjs.org/esbuild-openbsd-64/-/esbuild-openbsd-64-0.15.18.tgz",
"integrity": "sha512-yK5NCcH31Uae076AyQAXeJzt/vxIo9+omZRKj1pauhk3ITuADzuOx5N2fdHrAKPxN+zH3w96uFKlY7yIn490xQ==",
"dev": true,
"optional": true
},
@ -1336,30 +1336,30 @@
}
},
"esbuild-sunos-64": {
"version": "0.15.13",
"resolved": "https://registry.npmjs.org/esbuild-sunos-64/-/esbuild-sunos-64-0.15.13.tgz",
"integrity": "sha512-jVeu2GfxZQ++6lRdY43CS0Tm/r4WuQQ0Pdsrxbw+aOrHQPHV0+LNOLnvbN28M7BSUGnJnHkHm2HozGgNGyeIRw==",
"version": "0.15.18",
"resolved": "https://registry.npmjs.org/esbuild-sunos-64/-/esbuild-sunos-64-0.15.18.tgz",
"integrity": "sha512-On22LLFlBeLNj/YF3FT+cXcyKPEI263nflYlAhz5crxtp3yRG1Ugfr7ITyxmCmjm4vbN/dGrb/B7w7U8yJR9yw==",
"dev": true,
"optional": true
},
"esbuild-windows-32": {
"version": "0.15.13",
"resolved": "https://registry.npmjs.org/esbuild-windows-32/-/esbuild-windows-32-0.15.13.tgz",
"integrity": "sha512-XoF2iBf0wnqo16SDq+aDGi/+QbaLFpkiRarPVssMh9KYbFNCqPLlGAWwDvxEVz+ywX6Si37J2AKm+AXq1kC0JA==",
"version": "0.15.18",
"resolved": "https://registry.npmjs.org/esbuild-windows-32/-/esbuild-windows-32-0.15.18.tgz",
"integrity": "sha512-o+eyLu2MjVny/nt+E0uPnBxYuJHBvho8vWsC2lV61A7wwTWC3jkN2w36jtA+yv1UgYkHRihPuQsL23hsCYGcOQ==",
"dev": true,
"optional": true
},
"esbuild-windows-64": {
"version": "0.15.13",
"resolved": "https://registry.npmjs.org/esbuild-windows-64/-/esbuild-windows-64-0.15.13.tgz",
"integrity": "sha512-Et6htEfGycjDrtqb2ng6nT+baesZPYQIW+HUEHK4D1ncggNrDNk3yoboYQ5KtiVrw/JaDMNttz8rrPubV/fvPQ==",
"version": "0.15.18",
"resolved": "https://registry.npmjs.org/esbuild-windows-64/-/esbuild-windows-64-0.15.18.tgz",
"integrity": "sha512-qinug1iTTaIIrCorAUjR0fcBk24fjzEedFYhhispP8Oc7SFvs+XeW3YpAKiKp8dRpizl4YYAhxMjlftAMJiaUw==",
"dev": true,
"optional": true
},
"esbuild-windows-arm64": {
"version": "0.15.13",
"resolved": "https://registry.npmjs.org/esbuild-windows-arm64/-/esbuild-windows-arm64-0.15.13.tgz",
"integrity": "sha512-3bv7tqntThQC9SWLRouMDmZnlOukBhOCTlkzNqzGCmrkCJI7io5LLjwJBOVY6kOUlIvdxbooNZwjtBvj+7uuVg==",
"version": "0.15.18",
"resolved": "https://registry.npmjs.org/esbuild-windows-arm64/-/esbuild-windows-arm64-0.15.18.tgz",
"integrity": "sha512-q9bsYzegpZcLziq0zgUi5KqGVtfhjxGbnksaBFYmWLxeV/S1fK4OLdq2DFYnXcLMjlZw2L0jLsk1eGoB522WXQ==",
"dev": true,
"optional": true
},
@ -1582,9 +1582,9 @@
}
},
"typescript": {
"version": "4.8.4",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.8.4.tgz",
"integrity": "sha512-QCh+85mCy+h0IGff8r5XWzOVSbBO+KfeYrMQh7NJ58QujwcE22u+NUSmUxqF+un70P9GXKxa2HCNiTTMJknyjQ==",
"version": "4.9.4",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.4.tgz",
"integrity": "sha512-Uz+dTXYzxXXbsFpM86Wh3dKCxrQqUcVMxwU54orwlJjOpO3ao8L7j5lH+dWfTwgCwIuM9GQ2kvVotzYJMXTBZg==",
"dev": true
},
"wasm-feature-detect": {

View File

@ -46,10 +46,16 @@ export const startMapLibre = async (wasmPath: string | undefined, workerPath: st
}) : PoolWorker();
worker.onmessage = (message: MessageEvent) => {
// WARNING: Do not modify in_transfer_obj!
let in_transfer_obj = message.data;
// @ts-ignore TODO singlethreaded_main_entry may not be defined
maplibre.singlethreaded_main_entry(ptr, in_transfer_obj)
// WARNING: Do not modify data passed from Rust!
let in_transfer = message.data;
const main_entry = maplibre["singlethreaded_main_entry"];
if (!main_entry) {
throw Error("singlethreaded_main_entry is not defined. Maybe the Rust build used the wrong build configuration.")
}
main_entry(ptr, in_transfer)
}
return worker;

View File

@ -13,7 +13,13 @@ onmessage = async message => {
self.onmessage = async message => {
// This will queue further commands up until the module is fully initialised:
await initialised;
// @ts-ignore TODO may not exist
await maplibre.multithreaded_worker_entry(message.data);
const worker_entry = maplibre["multithreaded_worker_entry"]
if (!worker_entry) {
throw Error("multithreaded_worker_entry is not defined. Maybe the Rust build used the wrong build configuration.")
}
await worker_entry(message.data);
};
}

View File

@ -15,9 +15,17 @@ onmessage = async message => {
self.onmessage = async message => {
// This will queue further commands up until the module is fully initialised:
await initialised;
let procedure_ptr = message.data[0];
let input = message.data[1];
// @ts-ignore TODO
await maplibre.singlethreaded_worker_entry(procedure_ptr, input);
// WARNING: Do not modify data passed from Rust!
const procedure_ptr = message.data[0];
const input = message.data[1];
const worker_entry = maplibre["singlethreaded_worker_entry"];
if (!worker_entry) {
throw Error("singlethreaded_worker_entry is not defined. Maybe the Rust build used the wrong build configuration.")
}
await worker_entry(procedure_ptr, input);
};
}

View File

@ -1,15 +1,88 @@
//! Errors which can happen in various parts of the library.
//! Errors from JS world.
use js_sys::Error as JSError;
use std::{
borrow::Cow,
error::Error,
fmt::{Display, Formatter},
};
use js_sys::{Error as JSError, TypeError};
use maplibre::io::apc::{CallError, ProcedureError};
use thiserror::Error;
use wasm_bindgen::{JsCast, JsValue};
#[derive(Debug)]
pub struct WebError(pub String);
#[derive(Error, Debug)]
pub enum WebError {
#[error("JS error type is unknown")]
UnknownErrorType,
/// Returned if the message is not valid, e.g. if it it is not valid UTF-8.
#[error("message string in error is invalid")]
InvalidMessage,
/// TypeError like it is defined in JS
#[error("TypeError from JS")]
TypeError(Cow<'static, str>),
/// Any other Error
#[error("Error from JS")]
GenericError(Cow<'static, str>),
}
impl From<JsValue> for WebError {
fn from(maybe_error: JsValue) -> Self {
assert!(maybe_error.is_instance_of::<JSError>());
let error: JSError = maybe_error.dyn_into().unwrap(); // TODO: Remove unwrap
WebError(error.message().as_string().unwrap()) // TODO: remove unwrap
fn from(value: JsValue) -> Self {
if let Some(error) = value.dyn_ref::<TypeError>() {
let Some(message) = error
.message()
.as_string() else { return WebError::InvalidMessage; };
WebError::TypeError(message.into())
} else if let Some(error) = value.dyn_ref::<JSError>() {
let Some(message) = error
.message()
.as_string() else { return WebError::InvalidMessage; };
WebError::GenericError(message.into())
} else {
WebError::UnknownErrorType
}
}
}
/// Wraps several unrelated errors and implements Into<JSValue>. This should be used in Rust
/// functions called from JS-land as return error type.
#[derive(Debug)]
pub enum WrappedError {
ProcedureError(ProcedureError),
CallError(CallError),
WebError(WebError),
}
impl Display for WrappedError {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "Error from Rust: {:?}", self)
}
}
impl Error for WrappedError {}
impl From<WrappedError> for JsValue {
fn from(val: WrappedError) -> Self {
JsValue::from_str(&val.to_string())
}
}
impl From<CallError> for WrappedError {
fn from(e: CallError) -> Self {
WrappedError::CallError(e)
}
}
impl From<ProcedureError> for WrappedError {
fn from(e: ProcedureError) -> Self {
WrappedError::ProcedureError(e)
}
}
impl From<WebError> for WrappedError {
fn from(e: WebError) -> Self {
WrappedError::WebError(e)
}
}

View File

@ -11,7 +11,7 @@ use maplibre::{
use maplibre_winit::{WinitEnvironment, WinitMapWindowConfig};
use wasm_bindgen::prelude::*;
use crate::platform::http_client::WHATWGFetchHttpClient;
use crate::{error::WrappedError, platform::http_client::WHATWGFetchHttpClient};
mod error;
mod platform;
@ -65,7 +65,7 @@ type CurrentEnvironment = WinitEnvironment<
pub type MapType = Map<CurrentEnvironment>;
#[wasm_bindgen]
pub async fn run_maplibre(new_worker: js_sys::Function) {
pub async fn run_maplibre(new_worker: js_sys::Function) -> Result<(), WrappedError> {
let mut kernel_builder = KernelBuilder::new()
.with_map_window_config(WinitMapWindowConfig::new("maplibre".to_string()))
.with_http_client(WHATWGFetchHttpClient::new());
@ -77,31 +77,31 @@ pub async fn run_maplibre(new_worker: js_sys::Function) {
WHATWGFetchHttpClient::new(),
platform::multithreaded::pool_scheduler::WebWorkerPoolScheduler::new(
new_worker.clone(),
),
)?,
))
.with_scheduler(
platform::multithreaded::pool_scheduler::WebWorkerPoolScheduler::new(new_worker),
platform::multithreaded::pool_scheduler::WebWorkerPoolScheduler::new(new_worker)?,
);
}
#[cfg(not(target_feature = "atomics"))]
{
kernel_builder = kernel_builder
.with_apc(platform::singlethreaded::apc::PassingAsyncProcedureCall::new(new_worker, 4))
.with_apc(platform::singlethreaded::apc::PassingAsyncProcedureCall::new(new_worker, 4)?)
.with_scheduler(maplibre::io::scheduler::NopScheduler);
}
let kernel: Kernel<WinitEnvironment<_, _, _, ()>> = kernel_builder.build();
let mut map: MapType = Map::new(Style::default(), kernel).unwrap();
map.initialize_renderer(RendererBuilder::new())
.await
.unwrap();
let mut map: MapType = Map::new(Style::default(), kernel, RendererBuilder::new()).unwrap();
map.initialize_renderer().await.unwrap();
map.window_mut()
.take_event_loop()
.expect("Event loop is not available")
.run(map, None)
.run(map, None);
Ok(())
}
#[cfg(test)]

View File

@ -1,6 +1,6 @@
use async_trait::async_trait;
use js_sys::{ArrayBuffer, Uint8Array};
use maplibre::{error::Error, io::source_client::HttpClient};
use maplibre::io::source_client::{HttpClient, SourceFetchError};
use wasm_bindgen::{prelude::*, JsCast};
use wasm_bindgen_futures::JsFuture;
use web_sys::{Request, RequestInit, Response, WorkerGlobalScope};
@ -14,7 +14,7 @@ impl WHATWGFetchHttpClient {
Self {}
}
async fn fetch_array_buffer(url: &str) -> Result<JsValue, JsValue> {
async fn fetch_array_buffer(url: &str) -> Result<JsValue, WebError> {
let mut opts = RequestInit::new();
opts.method("GET");
@ -22,13 +22,15 @@ impl WHATWGFetchHttpClient {
// Get the global scope
let global = js_sys::global();
assert!(global.is_instance_of::<WorkerGlobalScope>());
let scope = global.dyn_into::<WorkerGlobalScope>().unwrap(); // TODO: remove unwrap
let scope = global
.dyn_into::<WorkerGlobalScope>()
.map_err(|_e| WebError::TypeError("Unable to cast to WorkerGlobalScope".into()))?;
// Call fetch on global scope
let maybe_response = JsFuture::from(scope.fetch_with_request(&request)).await?;
assert!(maybe_response.is_instance_of::<Response>());
let response: Response = maybe_response.dyn_into().unwrap(); // TODO: remove unwrap
let response: Response = maybe_response
.dyn_into()
.map_err(|_e| WebError::TypeError("Unable to cast to Response".into()))?;
// Get ArrayBuffer
let maybe_array_buffer = JsFuture::from(response.array_buffer()?).await?;
@ -38,8 +40,9 @@ impl WHATWGFetchHttpClient {
async fn fetch_bytes(&self, url: &str) -> Result<Vec<u8>, WebError> {
let maybe_array_buffer = Self::fetch_array_buffer(url).await?;
assert!(maybe_array_buffer.is_instance_of::<ArrayBuffer>());
let array_buffer: ArrayBuffer = maybe_array_buffer.dyn_into().unwrap(); // TODO: remove unwrap
let array_buffer: ArrayBuffer = maybe_array_buffer
.dyn_into()
.map_err(|_e| WebError::TypeError("Unable to cast to ArrayBuffer".into()))?;
// Copy data to Vec<u8>
let buffer: Uint8Array = Uint8Array::new(&array_buffer);
@ -58,9 +61,9 @@ impl Clone for WHATWGFetchHttpClient {
#[async_trait(?Send)]
impl HttpClient for WHATWGFetchHttpClient {
async fn fetch(&self, url: &str) -> Result<Vec<u8>, Error> {
async fn fetch(&self, url: &str) -> Result<Vec<u8>, SourceFetchError> {
self.fetch_bytes(url)
.await
.map_err(|WebError(msg)| Error::Network(msg))
.map_err(|e| SourceFetchError(Box::new(e)))
}
}

View File

@ -9,14 +9,19 @@ use rand::prelude::*;
use wasm_bindgen::prelude::*;
use web_sys::Worker;
use crate::error::WebError;
#[wasm_bindgen()]
extern "C" {
#[wasm_bindgen(js_name = newWorker)]
fn new_worker() -> JsValue;
}
type NewWorker = Box<dyn Fn() -> Result<Worker, WebError>>;
type Execute = Box<dyn (FnOnce() -> Promise) + Send>;
pub struct WorkerPool {
new_worker: Box<dyn Fn() -> Worker>,
new_worker: NewWorker,
state: Rc<PoolState>,
}
@ -35,7 +40,7 @@ impl PoolState {
}
pub struct Work {
func: Box<dyn (FnOnce() -> Promise) + Send>,
func: Execute,
}
impl Work {
@ -55,7 +60,7 @@ impl WorkerPool {
///
/// Returns any error that may happen while a JS web worker is created and a
/// message is sent to it.
pub fn new(initial: usize, new_worker: Box<dyn Fn() -> Worker>) -> Result<WorkerPool, JsValue> {
pub fn new(initial: usize, new_worker: NewWorker) -> Result<WorkerPool, WebError> {
let pool = WorkerPool {
new_worker,
state: Rc::new(PoolState {
@ -78,9 +83,9 @@ impl WorkerPool {
///
/// Returns any error that may happen while a JS web worker is created and a
/// message is sent to it.
fn spawn(&self) -> Result<(), JsValue> {
fn spawn(&self) -> Result<(), WebError> {
log::info!("spawning new worker");
let worker = (self.new_worker)();
let worker = (self.new_worker)()?;
// With a worker spun up send it the module/memory so it can start
// instantiating the wasm module. Later it might receive further
@ -104,7 +109,7 @@ impl WorkerPool {
///
/// Returns any error that may happen while a JS web worker is created and a
/// message is sent to it.
fn worker(&self) -> Result<Worker, JsValue> {
fn worker(&self) -> Result<Worker, WebError> {
let workers = self.state.workers.borrow();
let result = workers.choose(&mut thread_rng());
@ -130,7 +135,7 @@ impl WorkerPool {
///
/// Returns any error that may happen while a JS web worker is created and a
/// message is sent to it.
pub fn execute(&self, f: impl (FnOnce() -> Promise) + Send + 'static) -> Result<(), JsValue> {
pub fn execute(&self, f: impl (FnOnce() -> Promise) + Send + 'static) -> Result<(), WebError> {
let worker = self.worker()?;
let work = Work { func: Box::new(f) };
let work_ptr = Box::into_raw(Box::new(work));
@ -140,7 +145,7 @@ impl WorkerPool {
unsafe {
drop(Box::from_raw(work_ptr));
}
Err(e)
Err(e.into())
}
}
}

View File

@ -1,30 +1,29 @@
use std::future::Future;
use maplibre::{error::Error, io::scheduler::Scheduler};
use maplibre::{benchmarking::io::scheduler::ScheduleError, io::scheduler::Scheduler};
use wasm_bindgen::{prelude::*, JsCast};
use web_sys::Worker;
use super::pool::WorkerPool;
use crate::error::WebError;
pub struct WebWorkerPoolScheduler {
pool: WorkerPool,
}
impl WebWorkerPoolScheduler {
pub fn new(new_worker: js_sys::Function) -> Self {
// TODO: Are expects here oke?
pub fn new(new_worker: js_sys::Function) -> Result<Self, WebError> {
let pool = WorkerPool::new(
1,
Box::new(move || {
new_worker
.call0(&JsValue::undefined())
.expect("Unable to call new_worker function")
.map_err(WebError::from)?
.dyn_into::<Worker>()
.expect("new_worker function did not return a Worker")
.map_err(|_e| WebError::TypeError("Unable to cast to Worker".into()))
}),
)
.expect("Unable to create WorkerPool");
Self { pool }
)?;
Ok(Self { pool })
}
}
@ -32,7 +31,7 @@ impl Scheduler for WebWorkerPoolScheduler {
fn schedule<T>(
&self,
future_factory: impl (FnOnce() -> T) + Send + 'static,
) -> Result<(), Error>
) -> Result<(), ScheduleError>
where
T: Future<Output = ()> + 'static,
{
@ -43,6 +42,6 @@ impl Scheduler for WebWorkerPoolScheduler {
Ok(JsValue::undefined())
})
})
.map_err(|e| Error::Scheduler)
.map_err(|e| ScheduleError::Scheduling(Box::new(e)))
}
}

View File

@ -1,12 +1,15 @@
use maplibre::io::apc::CallError;
use wasm_bindgen::prelude::*;
use wasm_bindgen_futures::JsFuture;
use crate::platform::multithreaded::pool::Work;
use crate::{platform::multithreaded::pool::Work, WrappedError};
/// Entry point invoked by the worker.
#[wasm_bindgen]
pub async fn multithreaded_worker_entry(ptr: u32) -> Result<(), JsValue> {
pub async fn multithreaded_worker_entry(ptr: u32) -> Result<(), WrappedError> {
let work = unsafe { Box::from_raw(ptr as *mut Work) };
JsFuture::from(work.execute()).await?;
JsFuture::from(work.execute())
.await
.map_err(|_e| CallError::Schedule)?;
Ok(())
}

View File

@ -1,54 +1,71 @@
use std::{cell::RefCell, rc::Rc};
use js_sys::{ArrayBuffer, Uint8Array};
use log::error;
use maplibre::{
error::Error,
io::{
apc::{AsyncProcedure, AsyncProcedureCall, Context, Input, Message},
source_client::SourceClient,
},
use maplibre::io::{
apc::{AsyncProcedure, AsyncProcedureCall, CallError, Context, Input, Message, SendError},
source_client::SourceClient,
};
use rand::{prelude::SliceRandom, thread_rng};
use thiserror::Error;
use wasm_bindgen::{JsCast, JsValue};
use web_sys::{DedicatedWorkerGlobalScope, Worker};
use crate::platform::singlethreaded::{
transferables::FlatBufferTransferable, UsedContext, UsedHttpClient, UsedTransferables,
use crate::{
error::WebError,
platform::singlethreaded::{
transferables::FlatBufferTransferable, UsedContext, UsedHttpClient, UsedTransferables,
},
};
/// Error which happens during serialization or deserialization of the tag
#[derive(Error, Debug)]
#[error("failed to deserialize message tag")]
pub struct MessageTagDeserializeError;
#[derive(Debug)]
pub enum SerializedMessageTag {
pub enum MessageTag {
TileTessellated = 1,
LayerUnavailable = 2,
LayerTessellated = 3,
LayerIndexed = 4,
}
impl SerializedMessageTag {
impl MessageTag {
pub fn from_message(message: &Message<UsedTransferables>) -> Self {
match message {
Message::TileTessellated(_) => SerializedMessageTag::TileTessellated,
Message::LayerUnavailable(_) => SerializedMessageTag::LayerUnavailable,
Message::LayerTessellated(_) => SerializedMessageTag::LayerTessellated,
Message::LayerIndexed(_) => SerializedMessageTag::LayerIndexed,
Message::TileTessellated(_) => MessageTag::TileTessellated,
Message::LayerUnavailable(_) => MessageTag::LayerUnavailable,
Message::LayerTessellated(_) => MessageTag::LayerTessellated,
Message::LayerIndexed(_) => MessageTag::LayerIndexed,
}
}
pub fn from_u32(tag: u32) -> Option<Self> {
pub fn create_message(
&self,
transferable: FlatBufferTransferable,
) -> Message<UsedTransferables> {
// TODO: Verify that data matches tag
match self {
MessageTag::TileTessellated => {
Message::<UsedTransferables>::TileTessellated(transferable)
}
MessageTag::LayerUnavailable => {
Message::<UsedTransferables>::LayerUnavailable(transferable)
}
MessageTag::LayerTessellated => {
Message::<UsedTransferables>::LayerTessellated(transferable)
}
MessageTag::LayerIndexed => Message::<UsedTransferables>::LayerIndexed(transferable),
}
}
pub fn from_u32(tag: u32) -> Result<Self, MessageTagDeserializeError> {
match tag {
x if x == SerializedMessageTag::LayerUnavailable as u32 => {
Some(SerializedMessageTag::LayerUnavailable)
}
x if x == SerializedMessageTag::LayerTessellated as u32 => {
Some(SerializedMessageTag::LayerTessellated)
}
x if x == SerializedMessageTag::TileTessellated as u32 => {
Some(SerializedMessageTag::TileTessellated)
}
x if x == SerializedMessageTag::LayerIndexed as u32 => {
Some(SerializedMessageTag::LayerIndexed)
}
_ => None,
x if x == MessageTag::LayerUnavailable as u32 => Ok(MessageTag::LayerUnavailable),
x if x == MessageTag::LayerTessellated as u32 => Ok(MessageTag::LayerTessellated),
x if x == MessageTag::TileTessellated as u32 => Ok(MessageTag::TileTessellated),
x if x == MessageTag::LayerIndexed as u32 => Ok(MessageTag::LayerIndexed),
_ => Err(MessageTagDeserializeError),
}
}
}
@ -59,10 +76,10 @@ pub struct PassingContext {
}
impl Context<UsedTransferables, UsedHttpClient> for PassingContext {
fn send(&self, message: Message<UsedTransferables>) -> Result<(), Error> {
let tag = SerializedMessageTag::from_message(&message);
fn send(&self, message: Message<UsedTransferables>) -> Result<(), SendError> {
let tag = MessageTag::from_message(&message);
let transferable = FlatBufferTransferable::from_message(message);
let data = &transferable.data[transferable.start..];
let data = transferable.data();
let buffer = ArrayBuffer::new(data.len() as u32);
let byte_buffer = Uint8Array::new(&buffer);
@ -70,26 +87,15 @@ impl Context<UsedTransferables, UsedHttpClient> for PassingContext {
byte_buffer.set(&Uint8Array::view(data), 0);
}
let tag = tag as u32;
let global: DedicatedWorkerGlobalScope =
js_sys::global().dyn_into().map_err(|_e| Error::APC)?;
let transfer_obj = js_sys::Array::new();
transfer_obj.push(&JsValue::from(tag));
transfer_obj.push(&buffer);
let transfer = js_sys::Array::new();
transfer.push(&buffer);
// TODO: Verify transfer
let global: DedicatedWorkerGlobalScope = js_sys::global()
.dyn_into()
.map_err(|_e| SendError::Transmission)?;
global
.post_message_with_transfer(&transfer_obj, &transfer)
.map_err(|e| {
error!("{:?}", e);
Error::APC
})
.post_message_with_transfer(
&js_sys::Array::of2(&JsValue::from(tag as u32), &buffer),
&js_sys::Array::of1(&buffer),
)
.map_err(|_e| SendError::Transmission)
}
fn source_client(&self) -> &SourceClient<UsedHttpClient> {
@ -97,47 +103,48 @@ impl Context<UsedTransferables, UsedHttpClient> for PassingContext {
}
}
type NewWorker = Box<dyn Fn() -> Result<Worker, WebError>>;
pub type ReceivedType = RefCell<Vec<Message<UsedTransferables>>>;
#[derive(Error, Debug)]
pub enum PassingAPCError {
#[error("creating a worker failed")]
Worker,
}
pub struct PassingAsyncProcedureCall {
new_worker: Box<dyn Fn() -> Worker>,
workers: Vec<Worker>,
received: Rc<ReceivedType>, // FIXME (wasm-executor): Is RefCell fine?
received: Rc<ReceivedType>, // FIXME: Is RefCell fine?
}
impl PassingAsyncProcedureCall {
pub fn new(new_worker: js_sys::Function, initial_workers: u8) -> Self {
pub fn new(new_worker: js_sys::Function, initial_workers: usize) -> Result<Self, WebError> {
let received = Rc::new(RefCell::new(vec![]));
let received_ref = received.clone();
let create_new_worker = Box::new(move || {
let create_new_worker = || {
new_worker
.call1(
&JsValue::undefined(),
&JsValue::from(Rc::into_raw(received_ref.clone()) as u32),
)
.unwrap() // FIXME (wasm-executor): Remove unwrap
.map_err(WebError::from)?
.dyn_into::<Worker>()
.unwrap() // FIXME (wasm-executor): Remove unwrap
});
.map_err(|_e| WebError::TypeError("Unable to cast to Worker".into()))
};
let workers = (0..initial_workers)
.map(|_| {
let worker: Worker = create_new_worker();
let mut workers = Vec::with_capacity(initial_workers);
let array = js_sys::Array::new();
array.push(&wasm_bindgen::module());
worker.post_message(&array).unwrap(); // FIXME (wasm-executor): Remove unwrap
worker
})
.collect::<Vec<_>>();
for _ in 0..initial_workers {
let worker: Worker = create_new_worker()?;
Self {
new_worker: create_new_worker,
workers,
received,
let array = js_sys::Array::of1(&wasm_bindgen::module());
worker.post_message(&array).map_err(WebError::from)?;
workers.push(worker);
}
Ok(Self { workers, received })
}
}
@ -152,14 +159,23 @@ impl AsyncProcedureCall<UsedHttpClient> for PassingAsyncProcedureCall {
.pop()
}
fn call(&self, input: Input, procedure: AsyncProcedure<Self::Context>) {
let procedure_ptr = procedure as *mut AsyncProcedure<Self::Context> as u32; // FIXME (wasm-executor): is u32 fine, define an overflow safe function?
let input = serde_json::to_string(&input).unwrap(); // FIXME (wasm-executor): Remove unwrap
fn call(
&self,
input: Input,
procedure: AsyncProcedure<Self::Context>,
) -> Result<(), CallError> {
let procedure_ptr = procedure as *mut AsyncProcedure<Self::Context> as u32; // FIXME: is u32 fine, define an overflow safe function?
let input = serde_json::to_string(&input).map_err(|e| CallError::Serialize(Box::new(e)))?;
let array = js_sys::Array::new();
array.push(&JsValue::from(procedure_ptr));
array.push(&JsValue::from(input));
let message = js_sys::Array::of2(&JsValue::from(procedure_ptr), &JsValue::from(input));
self.workers[0].post_message(&array).unwrap(); // FIXME (wasm-executor): Remove unwrap
let worker = self
.workers
.choose(&mut thread_rng())
.ok_or(CallError::Schedule)?;
worker
.post_message(&message)
.map_err(|_e| CallError::Schedule)
}
}

View File

@ -1,4 +1,5 @@
use flatbuffers::FlatBufferBuilder;
use js_sys::{ArrayBuffer, Uint8Array};
use maplibre::{
benchmarking::tessellation::{IndexDataType, OverAlignedVertexBuffer},
coords::WorldTileCoords,
@ -57,11 +58,24 @@ pub mod tile_tessellated_generated {
}
pub struct FlatBufferTransferable {
pub data: Vec<u8>,
pub start: usize,
data: Vec<u8>,
start: usize,
}
impl FlatBufferTransferable {
pub fn from_array_buffer(buffer: ArrayBuffer) -> Self {
let buffer = Uint8Array::new(&buffer);
FlatBufferTransferable {
data: buffer.to_vec(),
start: 0,
}
}
pub fn data(&self) -> &[u8] {
&self.data[self.start..]
}
pub fn from_message(message: Message<UsedTransferables>) -> Self {
match message {
Message::TileTessellated(transferable) => transferable,
@ -220,7 +234,7 @@ impl LayerIndexed for FlatBufferTransferable {
}
fn to_tile_index(self) -> TileIndex {
TileIndex::Linear { list: vec![] } // TODO
TileIndex::Linear { list: vec![] } // TODO index
}
}

View File

@ -1,95 +1,79 @@
use std::{mem, rc::Rc};
use js_sys::{ArrayBuffer, Uint8Array};
use log::{error, info};
use js_sys::ArrayBuffer;
use maplibre::{
benchmarking::io::{
apc::{AsyncProcedure, Input, Message},
apc::{AsyncProcedure, Input},
source_client::{HttpSourceClient, SourceClient},
},
io::transferables::Transferables,
io::apc::CallError,
};
use thiserror::Error;
use wasm_bindgen::{prelude::*, JsCast};
use crate::{
error::WrappedError,
platform::singlethreaded::{
apc::{ReceivedType, SerializedMessageTag},
apc::{MessageTag, ReceivedType},
transferables::FlatBufferTransferable,
PassingContext, UsedContext, UsedTransferables,
PassingContext, UsedContext,
},
WHATWGFetchHttpClient,
};
/// Entry point invoked by the worker.
#[wasm_bindgen]
pub async fn singlethreaded_worker_entry(procedure_ptr: u32, input: String) -> Result<(), JsValue> {
pub async fn singlethreaded_worker_entry(
procedure_ptr: u32,
input: String,
) -> Result<(), WrappedError> {
let procedure: AsyncProcedure<UsedContext> = unsafe { mem::transmute(procedure_ptr) };
let input = serde_json::from_str::<Input>(&input).unwrap(); // FIXME (wasm-executor): Remove unwrap
let input =
serde_json::from_str::<Input>(&input).map_err(|e| CallError::Deserialize(Box::new(e)))?;
let context = PassingContext {
source_client: SourceClient::new(HttpSourceClient::new(WHATWGFetchHttpClient::new())),
};
let result = (procedure)(input, context).await;
if let Err(e) = result {
error!("{:?}", e); // TODO handle better
}
procedure(input, context).await?;
Ok(())
}
#[derive(Error, Debug)]
#[error("unable to deserialize message sent by postMessage()")]
pub struct DeserializeMessage;
/// Entry point invoked by the main thread.
#[wasm_bindgen]
pub unsafe fn singlethreaded_main_entry(
received_ptr: *const ReceivedType,
in_transfer_obj: js_sys::Array,
) -> Result<(), JsValue> {
// FIXME (wasm-executor): Can we make this call safe? check if it was cloned before?
in_transfer: js_sys::Array,
) -> Result<(), WrappedError> {
let tag = in_transfer
.get(0)
.as_f64()
.ok_or_else(|| CallError::Deserialize(Box::new(DeserializeMessage)))? as u32; // TODO: Is this cast fine?
let buffer: ArrayBuffer = in_transfer
.get(1)
.dyn_into()
.map_err(|_e| CallError::Deserialize(Box::new(DeserializeMessage)))?;
let tag = MessageTag::from_u32(tag).map_err(|e| CallError::Deserialize(Box::new(e)))?;
let message = tag.create_message(FlatBufferTransferable::from_array_buffer(buffer));
// FIXME: Can we make this call safe? check if it was cloned before?
let received: Rc<ReceivedType> = Rc::from_raw(received_ptr);
let tag = in_transfer_obj.get(0).as_f64().unwrap() as u32;
let tag = SerializedMessageTag::from_u32(tag).unwrap();
info!("singlethreaded_main_entry {:?}", tag);
let buffer: ArrayBuffer = in_transfer_obj.get(1).dyn_into().unwrap();
let buffer = Uint8Array::new(&buffer);
type TileTessellated = <UsedTransferables as Transferables>::TileTessellated;
type UnavailableLayer = <UsedTransferables as Transferables>::LayerUnavailable;
type IndexedLayer = <UsedTransferables as Transferables>::LayerIndexed;
let transferable = FlatBufferTransferable {
data: buffer.to_vec(),
start: 0,
};
// TODO: Verify that data matches tag
let message = match tag {
SerializedMessageTag::TileTessellated => {
Message::<UsedTransferables>::TileTessellated(transferable)
}
SerializedMessageTag::LayerUnavailable => {
Message::<UsedTransferables>::LayerUnavailable(transferable)
}
SerializedMessageTag::LayerTessellated => {
Message::<UsedTransferables>::LayerTessellated(transferable)
}
SerializedMessageTag::LayerIndexed => {
Message::<UsedTransferables>::LayerIndexed(transferable)
}
};
// MAJOR FIXME: Fix mutability
received
.try_borrow_mut()
.expect("Failed to borrow in singlethreaded_main_entry")
.push(message);
mem::forget(received); // FIXME (wasm-executor): Enforce this somehow
mem::forget(received); // FIXME: Enforce this somehow
Ok(())
}