mirror of
https://github.com/maplibre/maplibre-rs.git
synced 2025-12-08 19:05:57 +00:00
Add documentation
This commit is contained in:
parent
58c852f096
commit
92301d005e
@ -12,6 +12,14 @@ use crate::{
|
||||
window::MapWindowConfig,
|
||||
};
|
||||
|
||||
/// The environment defines which types must be injected into maplibre at compile time.
|
||||
/// Essentially, this trait implements the
|
||||
/// [dependency injection](https://en.wikipedia.org/wiki/Dependency_injection) design pattern.
|
||||
/// By instantiating this trait at compile time with concrete types, it is possible to create
|
||||
/// different compile-time instances of maplibre.
|
||||
///
|
||||
/// For example it is possible to change the way tasks are scheduled. It is also possible to change
|
||||
/// the HTTP implementation for fetching tiles over the network.
|
||||
pub trait Environment: 'static {
|
||||
type MapWindowConfig: MapWindowConfig;
|
||||
|
||||
|
||||
@ -97,7 +97,7 @@ impl HeadlessMap {
|
||||
coords: WorldTileCoords,
|
||||
source_layers: &[&str],
|
||||
) -> Result<StoredTile, Error> {
|
||||
let source_client = &self.kernel.source_client;
|
||||
let source_client = self.kernel.source_client();
|
||||
|
||||
let data = source_client.fetch(&coords).await?.into_boxed_slice();
|
||||
|
||||
|
||||
@ -20,9 +20,12 @@ use crate::{
|
||||
},
|
||||
};
|
||||
|
||||
/// The result of the tessellation of a tile.
|
||||
/// `TessellatedLayer` contains the result of the tessellation for a specific layer, otherwise
|
||||
/// `UnavailableLayer` if the layer doesn't exist.
|
||||
/// The result of the tessellation of a tile. This is sent as a message from a worker to the caller
|
||||
/// of an [`AsyncProcedure`].
|
||||
///
|
||||
/// * `TessellatedLayer` contains the result of the tessellation for a specific layer.
|
||||
/// * `UnavailableLayer` is sent if a requested layer is not found.
|
||||
/// * `TileTessellated` is sent if processing of a tile finished.
|
||||
#[derive(Clone)]
|
||||
pub enum Message<T: Transferables> {
|
||||
TileTessellated(T::TileTessellated),
|
||||
@ -30,12 +33,15 @@ pub enum Message<T: Transferables> {
|
||||
TessellatedLayer(T::TessellatedLayer),
|
||||
}
|
||||
|
||||
/// Inputs for an [`AsyncProcedure`]
|
||||
#[derive(Clone, Serialize, Deserialize)]
|
||||
pub enum Input {
|
||||
TileRequest(TileRequest),
|
||||
}
|
||||
|
||||
/// 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>);
|
||||
|
||||
fn source_client(&self) -> &SourceClient<HC>;
|
||||
@ -46,15 +52,62 @@ pub type AsyncProcedureFuture = Pin<Box<(dyn Future<Output = ()> + Send + 'stati
|
||||
#[cfg(not(feature = "thread-safe-futures"))]
|
||||
pub type AsyncProcedureFuture = Pin<Box<(dyn Future<Output = ()> + 'static)>>;
|
||||
|
||||
/// 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
|
||||
/// compile time. It is explicitly not possible to use closures, as they would require special
|
||||
/// serialization which is currently not supported.
|
||||
pub type AsyncProcedure<C> = fn(input: Input, context: C) -> AsyncProcedureFuture;
|
||||
|
||||
/// APCs define an interface for performing work asynchronously.
|
||||
/// This work can be implemented through procedures which can be called asynchronously, hence the
|
||||
/// name AsyncProcedureCall or APC for short.
|
||||
///
|
||||
/// APCs serve as an abstraction for doing work on a separate thread, and then getting responses
|
||||
/// back. An asynchronous procedure call can for example be performed by using message passing. In
|
||||
/// fact this could theoretically work over a network socket.
|
||||
///
|
||||
/// It is possible to schedule work on a remote host by calling [`AsyncProcedureCall::call()`]
|
||||
/// and getting the results back by calling the non-blocking function
|
||||
/// [`AsyncProcedureCall::receive()`]. The [`AsyncProcedureCall::receive()`] function returns a
|
||||
/// struct which implements [`Transferables`].
|
||||
///
|
||||
/// ## Transferables
|
||||
///
|
||||
/// Based on whether the current platform supports shared-memory or not, the implementation of APCs
|
||||
/// might want to send the whole data from the worker to the caller back or just pointers to that
|
||||
/// data. The [`Transferables`] trait allows developers to define that and use different data
|
||||
/// layouts for different platforms.
|
||||
///
|
||||
/// ## Message Passing vs APC
|
||||
///
|
||||
/// One might wonder why this is called [`AsyncProcedureCall`] instead of `MessagePassingInterface`.
|
||||
/// The reason for this is quite simple. We are actually referencing and calling procedures which
|
||||
/// are defined in different threads, processes or hosts. That means, that an [`AsyncProcedureCall`]
|
||||
/// is actually distinct from a `MessagePassingInterface`.
|
||||
///
|
||||
///
|
||||
/// ## Current Implementations
|
||||
///
|
||||
/// We currently have two implementation for APCs. One uses the Tokio async runtime on native
|
||||
/// targets in [`SchedulerAsyncProcedureCall`].
|
||||
/// For the web we implemented an alternative way to call APCs which is called
|
||||
/// [`PassingAsyncProcedureCall`]. This implementation does not depend on shared-memory compared to
|
||||
/// [`SchedulerAsyncProcedureCall`]. In fact, on the web we are currently not depending on
|
||||
/// shared-memory because that feature is hidden behind feature flags in browsers
|
||||
/// (see [here](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/SharedArrayBuffer).
|
||||
///
|
||||
///
|
||||
// TODO: Rename to AsyncProcedureCaller?
|
||||
pub trait AsyncProcedureCall<HC: HttpClient>: 'static {
|
||||
type Context: Context<Self::Transferables, HC> + Send;
|
||||
type Transferables: Transferables;
|
||||
|
||||
/// Try to receive a message non-blocking.
|
||||
fn receive(&self) -> Option<Message<Self::Transferables>>;
|
||||
|
||||
fn schedule(&self, input: Input, procedure: AsyncProcedure<Self::Context>);
|
||||
/// Call an [`AsyncProcedure`] using some [`Input`]. This function is non-blocking and
|
||||
/// returns immediately.
|
||||
fn call(&self, input: Input, procedure: AsyncProcedure<Self::Context>);
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
@ -101,7 +154,7 @@ impl<HC: HttpClient, S: Scheduler> AsyncProcedureCall<HC> for SchedulerAsyncProc
|
||||
Some(transferred)
|
||||
}
|
||||
|
||||
fn schedule(&self, input: Input, procedure: AsyncProcedure<Self::Context>) {
|
||||
fn call(&self, input: Input, procedure: AsyncProcedure<Self::Context>) {
|
||||
let sender = self.channel.0.clone();
|
||||
let client = self.http_client.clone(); // FIXME (wasm-executor): do not clone each time
|
||||
|
||||
|
||||
@ -3,13 +3,40 @@ use crate::{
|
||||
io::source_client::{HttpSourceClient, SourceClient},
|
||||
};
|
||||
|
||||
/// Holds references to core constructs of maplibre. Based on the compile-time initialization
|
||||
/// different implementations for handling windows, asynchronous work, or data sources are provided
|
||||
/// through a [`Kernel`].
|
||||
///
|
||||
/// An [`Environment`] defines the types which are used.
|
||||
///
|
||||
/// A Kernel lives as long as a [Map](crate::map::Map) usually. It is shared through out various
|
||||
/// components of the maplibre library.
|
||||
pub struct Kernel<E: Environment> {
|
||||
pub map_window_config: E::MapWindowConfig,
|
||||
pub apc: E::AsyncProcedureCall,
|
||||
pub scheduler: E::Scheduler,
|
||||
pub source_client: SourceClient<E::HttpClient>,
|
||||
map_window_config: E::MapWindowConfig,
|
||||
apc: E::AsyncProcedureCall,
|
||||
scheduler: E::Scheduler,
|
||||
source_client: SourceClient<E::HttpClient>,
|
||||
}
|
||||
|
||||
impl<E: Environment> Kernel<E> {
|
||||
pub fn map_window_config(&self) -> &E::MapWindowConfig {
|
||||
&self.map_window_config
|
||||
}
|
||||
|
||||
pub fn apc(&self) -> &E::AsyncProcedureCall {
|
||||
&self.apc
|
||||
}
|
||||
|
||||
pub fn scheduler(&self) -> &E::Scheduler {
|
||||
&self.scheduler
|
||||
}
|
||||
|
||||
pub fn source_client(&self) -> &SourceClient<E::HttpClient> {
|
||||
&self.source_client
|
||||
}
|
||||
}
|
||||
|
||||
/// A convenient builder for [Kernels](Kernel).
|
||||
pub struct KernelBuilder<E: Environment> {
|
||||
map_window_config: Option<E::MapWindowConfig>,
|
||||
apc: Option<E::AsyncProcedureCall>,
|
||||
|
||||
@ -102,7 +102,7 @@ where
|
||||
where
|
||||
E: Environment<MapWindowConfig = MWC>,
|
||||
{
|
||||
self.initialize(&kernel.map_window_config).await
|
||||
self.initialize(kernel.map_window_config()).await
|
||||
}
|
||||
}
|
||||
|
||||
@ -123,7 +123,7 @@ impl<MWC: MapWindowConfig> UninitializedRenderer<MWC> {
|
||||
where
|
||||
E: Environment<MapWindowConfig = MWC>,
|
||||
{
|
||||
self.initialize_headless(&kernel.map_window_config).await
|
||||
self.initialize_headless(kernel.map_window_config()).await
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -35,7 +35,7 @@ impl<E: Environment> Stage for PopulateTileStore<E> {
|
||||
..
|
||||
}: &mut MapContext,
|
||||
) {
|
||||
if let Some(result) = self.kernel.apc.receive() {
|
||||
if let Some(result) = self.kernel.apc().receive() {
|
||||
match result {
|
||||
Message::TileTessellated(tranferred) => {
|
||||
let coords = tranferred.coords();
|
||||
|
||||
@ -150,7 +150,8 @@ impl<E: Environment> RequestStage<E> {
|
||||
coords: WorldTileCoords,
|
||||
layers: &HashSet<String>,
|
||||
) -> Result<(), Error> {
|
||||
/* if !tile_repository.is_layers_missing(coords, layers) {
|
||||
/* TODO: is this still required?
|
||||
if !tile_repository.is_layers_missing(coords, layers) {
|
||||
return Ok(false);
|
||||
}*/
|
||||
|
||||
@ -158,7 +159,7 @@ impl<E: Environment> RequestStage<E> {
|
||||
tile_repository.create_tile(coords);
|
||||
|
||||
tracing::info!("new tile request: {}", &coords);
|
||||
self.kernel.apc.schedule(
|
||||
self.kernel.apc().call(
|
||||
Input::TileRequest(TileRequest {
|
||||
coords,
|
||||
layers: layers.clone(),
|
||||
|
||||
@ -185,7 +185,7 @@ impl AsyncProcedureCall<UsedTransferables, UsedHttpClient> for PassingAsyncProce
|
||||
self.received.pop()
|
||||
}
|
||||
|
||||
fn schedule(&self, input: Input, procedure: AsyncProcedure<Self::Context>) {
|
||||
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
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user