Rustic Maps SDK
Rustic Maps SDK is a native map SDK written in Rust. It gives your application map rendering, search, routing, turn-by-turn navigation, and offline maps from one Rust core, delivered to Android (Kotlin), iOS (Swift), and React Native through UniFFI’s UDL-first bindings. WebAssembly is a first-class target, so the same core renders map tiles in a browser with WebGPU.
What the SDK gives you
One contract, four platforms
The FFI surface is a single frozen UniFFI UDL file. The Kotlin, Swift, and React Native Turbo Module bindings are generated from it, and the WebAssembly TypeScript mirror is regression-checked against it. The contract changes addition-only: existing members keep their signature and meaning.
Search, routing, and traffic
Ten vendor-neutral provider contracts cover geocoding (forward and reverse), routing, the distance matrix, place search, traffic incidents, traffic flow, elevation, raster tiles, vector tiles, and offline routing. A full map-platform provider implementation backs them, rate-limited and retry-aware, with a disk response cache under your own byte budget.
A rendering core that draws anywhere
The renderer spans web-mercator projection, an anchored camera with
animations and fit-bounds framing, exact frustum tile listing, a
frame-driven tile scheduler with per-frame budgets and parent fallback,
and a byte-budget tile texture pool feeding a wgpu renderer. The same
scene draws headless in tests, on Android and iOS surfaces, and in the
browser through WebGPU.
Turn-by-turn navigation
The navigation state machine follows the Ferrostar model. It composes banner and spoken instructions verbatim from the provider’s text, publishes speech-instruction triggers at documented distance bands, and hands deviation handling and reroute policy to you at clear seams.
Offline maps
A deterministic compiler turns an OpenStreetMap PBF extract into a compact self-describing routing archive. An offline routing provider answers routes over the memory-mapped graph, honoring recorded turn restrictions, and wraps the online provider as an automatic fallback.
Structured diagnostics
A bounded, per-subsystem diagnostics ring streams structured log events to a foreign listener with coordinate scrubbing at the sink. Readbacks expose frame tile statistics, cache terms, and provider-failure records, so a screen in your product can show exactly what the SDK is doing.
Architecture at one glance
| Layer | What it is |
|---|---|
geospatial-types | The record and math layer: coordinates, bounding boxes, routes, places, traffic, tiles, and the typed error model |
geospatial-traits | The ten provider contracts every provider implementation fulfills |
map-core, map-cache | Provider resolution with cache-then-network behavior, backed by a SQLite response cache with per-category time-to-live and byte-budget eviction |
mapquest-provider | The map-platform provider implementation (rate-limited, retry-aware, fixture-tested) |
map-rendering | Projection, camera, tile scheduler, texture pool, and the wgpu renderer |
map-navigation | The turn-by-turn trip-state machine and its instruction composition |
map-offline-graph-compiler, map-offline-graph-archive | OpenStreetMap extracts compiled into the offline routing archive and its memory-mapped reader |
map-native-core | The UniFFI binding crate: the frozen UDL contract is the single source of truth |
map-web-bindings | The wasm-bindgen surface for the browser |
The full call surface these crates expose across the FFI is the FFI contract; each member’s documentation lives in the generated API reference.
Requirements at a glance
- Android: API level 24 or higher. The release AAR is 16 MiB across three ABIs (8.7 to 13 MiB each after strip).
- iOS: iOS 17 or higher. The XCFramework ships two arm64 slices.
- React Native: a source TurboModule package compiled against your application’s own React Native version.
- WebAssembly: any browser with WebGPU; the wasm package is 3.6 MiB.
Per-platform tables, artifact sizes, and the version policy are in Requirements and distributions.
Where to go next
- Install the artifacts and build a rendered map: Your first map.
- Resolve places and plan a route: Search and routing.
- Simulate a guided turn-by-turn drive: Turn-by-turn navigation.
- Keep rendering and routing working without a network: Offline maps.
- See the core running from a browser, no build step: Live demo.
Status and license
The SDK is in active development. The canonical short link is https://sdk.mapquest.live, which serves this site in place (subpaths preserved, so the hostname stays in your browser). The rendered site is rebuilt from the source repository’s build pipeline; a section appears here only once the pipeline that produces it is green, and each page carries the build’s provenance stamp.
The SDK is dual-licensed under MIT OR Apache-2.0. Map imagery © MapQuest; map data © OpenStreetMap contributors (ODbL). See third-party notices for the full attribution and license table, and keep the attribution visible in your application.
Requirements and distributions
The SDK ships as release archives you download and install into your project; it publishes through no package registry. This page lists what each platform needs to build against the SDK, what the artifacts weigh, and how versions are stamped. The Downloads page names the released files.
Android
| Requirement | Value |
|---|---|
| Minimum API level | 24 |
| Compile SDK | 36 |
| Android Gradle Plugin | 9.0.0 (Gradle wrapper 9.5.1) |
| Kotlin binding runtime | net.java.dev.jna:jna:5.18.1@aar (UniFFI runtime) |
| Coroutines | org.jetbrains.kotlinx:kotlinx-coroutines-core:1.10.2 |
The Android artifact is a release AAR (Maven coordinate
com.angoratek:rustic-maps-sdk) carrying the Kotlin bindings plus the
Rust dynamic library for three ABIs. Measured sizes (2026-10-29): the AAR
is 16 MiB; the stripped per-ABI .so libraries measure arm64-v8a
12 MiB, armeabi-v7a 8.7 MiB, and x86_64 13 MiB.
iOS
| Requirement | Value |
|---|---|
| Deployment target | iOS 17 or higher |
| Swift tooling | swift-tools-version: 5.10 (Swift 5 language mode) |
| Architectures | arm64 (device) and arm64 (simulator) |
The iOS artifact is an XCFramework containing the static library, its
headers and modulemaps, consumed by Swift Package Manager through the
release’s checksummed zip (the RusticMapsSDK product). Measured sizes
(2026-10-29): 217 MiB per slice, 207 MiB after strip -x. A static
archive carries every compiled object and the consumer’s final link
discards unused code, so these archive sizes are not the size your
application adds; the per-architecture measurement is a recorded open
decision on whether the iOS artifact later moves to a dynamic slice.
React Native
The React Native artifact is a source TurboModule package
(@angoratek/rustic-maps-sdk) generated from the frozen contract. It
carries the generated TypeScript surface, the JSI C++ glue, and the
podspec/CMake wiring your application compiles against its own React
Native version, so no binary slice inside the tarball constrains which
React Native version you run. The Android side of the current package
builds the Rust static archive for arm64-v8a.
WebAssembly
The wasm artifact (@angoratek/rustic-maps-sdk-web) is the
wasm-bindgen ES module emit. Measured size (2026-10-29): 3.6 MiB
downloaded, of which the wasm module is 3.4 MiB.
The browser surface renders through WebGPU. Chromium-based browsers have
supported it flag-free since 113 (2023); Safari ships it from version 26;
Firefox ships it since 141, with desktop first. WebGPU is a
secure-context API, so your page must be served over HTTPS or from
localhost, and navigator.gpu is the feature test to use — no
user-agent sniffing. MDN’s WebGPU page carries the live compatibility
table.
Versions policy
- Pin exact versions. Release archives are immutable and carry a
version in their file name (
rustic-maps-sdk-android-v0.4.0.aarand the like); keep the version you validated against, and upgrade deliberately. - One version line. The Cargo workspace version is the single source
of truth: the Android Maven publication, the npm packages, and the
Swift manifest are all stamped from it at release time. The compiled SDK
also answers the version probe (
getSdkCompiledVersionacross the FFI), so your diagnostics screen can verify the loaded library matches the artifacts you shipped. - The FFI contract changes addition-only. Existing members keep their signature and meaning, so a minor-version upgrade keeps your compiled bindings valid; packaging changes (an artifact set or renamed target) bump the minor version while the release is 0.x.
- Semantic versioning from 0.1.0. Versioned tags (
vX.Y.Z) are the release trigger; tags are never moved, and a broken release is superseded by a new tag. - Checksums and signatures. Every release carries a per-asset SHA-256 checksum, an aggregated checksum manifest, and Sigstore signature bundles; verifying them is part of the Downloads flow.
Where the numbers come from: the sizes and tool versions above are copied from the packaging design’s recorded measurements and the build scripts that produce the artifacts, and they travel with the release notes of each tag.
Performance and profiling
This page is integration guidance: the practices that keep map rendering, routing, and navigation responsive, and the readbacks the SDK exposes so you can measure your own integration on your own hardware and scenes. The project publishes no benchmark numbers; any figure you quote should come from your own device and your own route mix.
Construct the SDK once
- Build one
MapSdkobject and register your providers once at startup. Re-registration replaces every slot (including a cached tile slot), so an accidental double registration silently reopens the cache and resets its tuning: register, then tune withsetRasterTileCacheTimeToLive. - Keep a single
MapNavigationControlleraround the lifetime of a trip screen; subscriptions are explicit and cheap, but re-subscribing listeners is wasted work on every frame.
Frame the camera with the contract’s helpers
- Prefer the
fit_boundscamera constructor for route previews and search-result framing. It computes a top-down, north-up camera that keeps the whole box visible inside the insets you give it, instead of a hand-tuned zoom level that breaks on another screen size. - Give it real insets. Route cards, attribution, and sheets are overlays your application owns; the insets tell the camera what the map may not hide. The insets-aware framing is what keeps the destination inside the visible region on every device.
- Read the camera back with the attribute getters (
center,zoomLevel,bearingDegrees,pitchDegrees) when you need to persist or compare a pose; the camera carries no setters, so a new pose is a new constructor call.
Let the render loop stay explicit
- Frames are explicit by design: the platform view drives
drawfrom its own cadence (a display link or choreographer callback). Do not add a second render loop; serialize draws and never calldrawagain while a previous call is still in flight — a draw in flight reports the next frame busy, and the cadence retries it. - Poll
frameSettledin the render pump to skipdrawwhen a re-draw would present the same pixels: true when the camera, overlay list, and location indicator are unchanged and no pending tile pixels remain. This keeps battery cost near zero on a static map. - Read
TileFrameStatisticsfrom every draw. The four readiness counts (present,fallback,deferred,unavailable) always add up to the requested count, so a persistent slice ofdeferredtells you your per-frame fetch budget is the limiter on your network, and a slice ofunavailablepoints at provider refusals rather than slow ones. - Feed gesture deltas through the SDK’s camera seam (pan by pixels, focal-point zoom and rotation) instead of moving the viewport yourself; the SDK clamps zoom and pitch through the camera’s own ranges and holds the focal point exactly.
Size the tile cache deliberately
- Register the tile cache with an absolute byte budget that fits your product (the reference application’s demo budget is 16 MiB for the whole recorded fixture set). The budget is a registration-time fact: retuning it means re-registering.
- Set the tile time-to-live live through
setRasterTileCacheTimeToLive. Imagery-provider terms put a ceiling on what the cache may retain; a request above the ceiling is accepted and the ceiling clamps what is stored. ReadrasterTileCacheTermsto display the effective value, the ceiling, and the default in a settings or diagnostics screen. - Do not set a zero time-to-live; the call is refused because it would expire every entry on capture.
Keep the provider path under your quota
- The provider implementation carries a shared token-bucket rate limiter
and bounded idempotent retries. Treat
RateLimitExhaustedas “retry after the next refill”, not as an error to surface in the UI. - Cache-then-network is the default shape of every provider call, so repeated identical requests inside the time-to-live cost no quota. The tile category defaults to a 30-day time-to-live, the imagery terms ceiling.
- Batch what the contracts batch: the distance matrix answers every origin-destination pair in one call; fetching matrix cells one by one multiplies your request count for the same data.
Use offline where it pays
- The offline routing provider answers routes over a memory-mapped archive with A*; no network round trip. Compile an extract sized to the region your users drive in, not the planet: the compiler is deterministic and the archive is self-describing.
- The routing decorator retries over the offline graph when the online provider fails, so a coverage-limited archive is a graceful degradation, not a failure mode. Its maneuver texts stay honest where the archive carries no street names.
Profile with the SDK’s own readbacks
- Subscribe to diagnostics events at
Infoseverity (orDebugfor frame-path detail). Events stream fire-and-forget from bounded ring buffers, coordinates are scrubbed at the sink, and a slow listener can never slow the SDK. - Drain the backlog with the bounded flush readback from a non-frame context: the flush is synchronous and must stay out of the render path.
- For provider failures, read
latestProviderFailureDiagnosticsafter a suspicious session; the ring carries the contract, method, timestamp, and verbatim failure text for every failed foreign-callback provider call. - Measure on your target device and your real scenes. The SDK reports what it did (tile counts, frame statistics, cache terms); the timing numbers that matter are the ones your profiling run produces.
Live demo
The demo runs the SDK’s own browser build — the same wasm module the
WebAssembly package ships — drawing rendered map imagery on a WebGPU canvas
with pan, zoom, bearing and pitch controls, in-page overlays, and offline
routing over a compiled archive. It is static, keyless and served from this
site’s /demo path.
What the demo needs in a browser
- WebGPU. Chromium-based browsers support it flag-free since 113; Safari ships it from version 26; Firefox ships it since 141, with desktop first. MDN’s WebGPU page carries the live compatibility table.
- A secure context. The browser resolves WebGPU only over HTTPS or localhost.
- Keyless by default. The demo renders recorded fixture imagery and answers routes over a committed archive. The Provider credentials panel can take a provider key at runtime for live geocoding, routing and tile probes (MapQuest today; the provider registry is designed for more); the key stays in the page’s memory and is never persisted.
Running the same loop locally
Every tutorial in the Guides group ships a WebAssembly tab with the same surface the demo shows, so you can run the stack yourself from the wasm package: install it once and the Your first map tutorial covers the whole loop. The browser harness the demo is built from — and the fixture routes it serves — is described in How the demo loop runs.
How it works
The SDK is one Rust core behind a frozen interface. This page explains the concepts that interface exposes, so the tutorials’ calls read as applications of them rather than recipes to copy. Every concept below is part of the frozen FFI contract.
Coordinates and the camera
A position is a GeographicCoordinate: latitude and longitude in decimal
degrees on the WGS 84 ellipsoid. A region is a BoundingBox between a
south-west and a north-east corner, and a box never spans the
antimeridian.
The camera is a MapCamera value: an anchored pose of center, zoom,
bearing, and pitch. The ground point under the center sits at the
viewport center, zoom and pitch are clamped, and bearing wraps into the
range from 0 up to but not including 360 degrees.
| Camera attribute | What it means |
|---|---|
center | The coordinate anchored at the viewport center |
zoom_level | Scale; a continuous level, not necessarily a whole number |
bearing_degrees | Rotation clockwise from north |
pitch_degrees | Tilt from top-down (0) toward the horizon |
A camera carries no setters. A new pose is a new constructor call, and
the attribute getters read the state back as it was built. The second
constructor, fit_bounds, frames a bounding box inside the inset region
you give it: top-down, north-up, zoomed to the largest level that keeps
the whole box visible, and anchored on the inset region’s center. Insets
are the pixels your own overlays keep clear, so route cards and
attribution never hide the destination.
The camera answers screen mappings without drawing. project_to_screen
and screen_to_ground_coordinate convert between a coordinate and
viewport pixels, visible_tile_keys lists the tiles the view covers, and
ground_meters_per_screen_pixel sizes a scale bar.
Zoom levels and tiles
A tile is one payload at a fixed zoom, addressed by a TileKeyRecord: a
column, a row counted from the north, and the zoom level. The addressing
is the web-mercator XYZ scheme. At zoom 0 the world is one tile, and each
level doubles the columns and rows.
The camera’s zoom is continuous, so it answers two different questions.
tile_zoom_level gives the nearest whole level a tile fetch should use.
visible_tile_keys gives every tile at a chosen level that intersects
the visible ground, sorted row-major from the north.
A raster tile is a pre-rendered image. The contract carries it as
RasterTileData with its encoded bytes, an image format (PNG or JPEG),
and a resolution: Standard, or Retina for the doubled-texel @2x
form. A vector tile carries geometry and style separately from the
pixels and is rendered on the device. The shipped renderer draws raster
imagery, and vector rendering is a later path.
The provider registry
Provider-backed capability reaches you through MapSdk, an object with
no providers at construction. You register implementations, then call
the contract methods: forward and reverse geocoding, routing, the
distance matrix, place search, traffic incidents, traffic flow, and
raster tiles. Every call resolves through a registry. A call into a
contract with no registration throws ProviderNotConfigured naming the
contract, and the elevation contract has no registered implementation
today.
There are two registration paths:
register_mapquest_providersinstalls the whole map-platform provider set from one API key, with the SDK defaults for rate limiting and retries.register_geocoding_provider_from_foreignandregister_raster_tile_provider_from_foreigninstall an implementation your own Kotlin or Swift code supplies through theGeocodingProviderandRasterTileProviderinterfaces.
Registration is per object, and re-registration replaces every slot. A
second MapSdk object needs its own registration.
The cache path
register_mapquest_providers_with_tile_cache installs the same provider
set and wraps the raster-tile contract in a disk response cache: a SQLite
database at a path you own, under an absolute byte budget. A cache hit
answers from disk, and a cache miss goes to the network and back-fills.
Cached tile responses are retained under the tiles category’s
time-to-live, 30 calendar days by default. That default is also the
imagery provider’s terms ceiling. set_raster_tile_cache_time_to_live
tunes the value, an override above the ceiling is accepted and clamped,
and raster_tile_cache_terms reads back three values: the effective
time-to-live, the provider’s declared ceiling, and the category default.
The byte budget is fixed when the store opens, so retuning it means
registering again, and a fresh registration starts at the category
default.
Raster imagery is the layer the cache holds. Offline routing is a separate layer with its own archive (see below).
The frozen contract and four platforms
One UniFFI UDL file defines the surface, and it is the single source of truth. The Kotlin, Swift, and React Native bindings are generated from it, and the WebAssembly TypeScript mirror is regression-checked against it. The contract changes addition-only: existing members keep their signature and meaning, and new capability arrives as new members.
| Platform | How you call the contract |
|---|---|
| Android (Kotlin) | Suspend functions and typed records; failures are the contract’s SdkError variants |
| iOS (Swift) | Async throwing methods and records |
| React Native | Promise-returning TurboModule methods with lower camel case field names |
| WebAssembly | A hand-reviewed TypeScript mirror over wasm-bindgen, covering the camera, rendering, and offline-routing slice |
Plain data crosses as records and enums. The error model crosses as the
SdkError variants the failing call can produce, with verbatim detail
text. get_sdk_compiled_version on every surface answers the compiled
SDK version, so a diagnostics screen can compare the loaded core with the
artifacts your application shipped.
Navigation state
Routing returns a RouteData: legs between consecutive waypoints, and
steps inside each leg, each carrying its distance, duration, maneuver
label, and instruction texts. Navigation consumes that value rather than
extending it.
MapNavigationController holds a trip. You start one with
start_trip(route, unit_system, step_advance_policy, deviation_policy)
and deliver one position fix at a time with provide_location_update.
The controller publishes four topics: banner instructions, speech
triggers, deviation status, and phase changes. A subscription is
cancelled explicitly through its handle.
A trip has three phases: Navigating, OffRoute, and Arrived.
Rerouting is not a phase; a trip moves between navigating and off route
until it arrives. Deviation has two grades:
FullyOffRoutefreezes progress until a graded fix recovers or a reroute replaces the route.OffStepOnRoutemeans the fix is off the current step but still on a later part of the route, and the trip keeps navigating.
Guidance uses two policies you choose at trip start. StepAdvancePolicy
decides when the trip moves to the next step, and DeviationPolicy
decides when a fix counts as off route. A fix without a reported
accuracy never advances a step and is never judged for deviation, so a
simulated source must carry accuracy like a real one.
Instruction text has two channels. The banner channel carries the
display text, and the speech channel carries the text-to-speech text
through SpeechInstruction. The SDK decides when a line is newsworthy
and supplies the text verbatim. Your application owns playback and
synthesis, and utterance_id is a stable identity for deduplication.
A deviation asks for a replacement route through a small gate.
begin_reroute_calculation answers true when the request passes the
in-flight, cooldown, and movement checks, provide_reroute applies the
replacement, and cancel_reroute_calculation abandons it. The trip
state’s is_calculating_new_route reports the in-flight fact.
Offline routing
Offline routing answers from a compiled archive rather than a network round trip. The archive is built from an OpenStreetMap extract and is self-describing. It answers routes with an A* search over a memory-mapped graph and honors recorded turn restrictions. Maneuver text derives from the geometry where the extract carries no street names.
The archive’s direct route call joins the native contract in a later slice. Today it answers the automatic fallback when the online provider fails and the WebAssembly surface’s offline route seam, and one compiled archive serves every target.
Where to go next
The tutorials apply these concepts in order: Your first map, Search and routing, Turn-by-turn navigation, and Offline maps.
Your first map
Install the SDK, register a map provider with your API key, and draw a first rendered map surface. Choose your platform’s tab and follow its steps top to bottom; every snippet is a complete step and compiles against the frozen FFI contract.
Before you start
Android Studio with the SDK 36 toolchain, a project with
minSdk = 24, and a downloaded release AAR copied into
app/libs/, and a map-platform API key for the provider you register.
Install the artifact
In your application module’s build.gradle.kts, add the local AAR and
the two runtime dependencies the Kotlin bindings need:
repositories {
flatDir { dirs("libs") }
}
dependencies {
implementation(files("libs/rustic-maps-sdk-android-v0.1.0.aar"))
implementation("net.java.dev.jna:jna:5.18.1@aar") // the UniFFI runtime
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.10.2")
}
The version in the file name is the release you downloaded; keep it exact.
Build the SDK and register the provider
Construct the SDK object and install the map-platform provider set in one call, as the reference application does at startup:
import uniffi.map_native_core.MapSdk
val mapSdk = MapSdk()
mapSdk.registerMapquestProviders(mapQuestApiKey)
The call throws verbatim when the key is refused, so surface it in your error state rather than continuing.
Read the key at runtime — from your app’s own secure configuration or a settings surface — never from source and never baked into the build. The reference application’s settings view takes it at runtime, and its UI automation injects it through a launch intent. The registration call itself is explicit, as shown.
Draw a map surface
The SDK renders onto a platform SurfaceView you own. Three parts:
-
A small JNI shim that hands the SDK the
ANativeWindow*pointer of your surface (the pointer has no public Kotlin API):// src/main/cpp/native_window_shim.c — the whole shim: #include <android/bitmap.h> #include <android/native_window.h> #include <android/native_window_jni.h> #include <jni.h> JNIEXPORT jlong JNICALL Java_com_example_map_nativeWindowPointerFromSurface(JNIEnv* env, jobject, jobject surface) { ANativeWindow* window = ANativeWindow_fromSurface(env, surface); return (jlong) window; // release it again on surface-destroyed } -
The Kotlin declaration beside your module wiring:
external fun nativeWindowPointerFromSurface(surface: Surface): Long -
The surface host: construct the view over the SDK, attach once the surface exists, then pump one
drawper composed frame:import kotlinx.coroutines.withFrameNanos import uniffi.map_native_core.MapCamera import uniffi.map_native_core.MapSurfaceTargetHandle import uniffi.map_native_core.MapSurfaceView val mapSurface = MapSurfaceView(mapSdk) // On the platform surface's created callback (once): val nativeWindowPointer = nativeWindowPointerFromSurface(surface) mapSurface.attach( MapSurfaceTargetHandle( appkitViewPointer = null, iosViewPointer = null, androidNativeWindowPointer = nativeWindowPointer.toULong(), ), widthPixels = surfaceWidthPixels.toUInt(), heightPixels = surfaceHeightPixels.toUInt(), ) // The per-frame pump (the SDK deliberately has no render loop): withFrameNanos { _ -> try { mapSurface.draw( MapCamera( center, zoomLevel = 12.0, bearingDegrees = 0.0, pitchDegrees = 0.0, ), ) } catch (drawException: Exception) { // A draw in flight reports busy: retry on the next frame. } }
Call mapSurface.resize(width, height) from your surface-changed
callback and mapSurface.detach() on surface-destroyed.
Add your first overlay
Draw more than tiles by setting the overlay list the surface draws every frame. A route polyline over two points:
import uniffi.map_native_core.GeographicCoordinate
import uniffi.map_native_core.MapOverlayDescriptor
import uniffi.map_native_core.MapOverlayKind
import uniffi.map_native_core.OverlayColor
mapSurface.setOverlays(
listOf(
MapOverlayDescriptor(
kind = MapOverlayKind.ROUTE_POLYLINE,
coordinates = listOf(
GeographicCoordinate(latitude = 47.3769, longitude = 8.5417),
GeographicCoordinate(latitude = 47.3774, longitude = 8.5411),
),
rgbaColor = OverlayColor(red = 0.2, green = 0.4, blue = 0.9, alpha = 0.9),
widthPixels = 3.0,
),
),
)
Before you start
Xcode with the Swift 5.10 tooling the packaging pins
(swift-tools-version: 5.10), a project with an iOS 17 deployment
target, and a downloaded release XCFramework, and a
map-platform API key for the provider you register.
Unzip the release into a Frameworks/ directory in your project and add
the Swift package product (RusticMapsSDK) through your package
manifest, resolved from the release’s checksummed zip:
// Package.swift
// swift-tools-version: 5.10
import PackageDescription
let package = Package(
name: "MyMapApp",
dependencies: [
// The release's Package.swift lands on the repository's
// swift-package branch; its binaryTarget pins the release URL
// and checksum.
],
targets: [
.executableTarget(name: "MyMapApp")
]
)
Until the release lands, the checked-in packaging manifest in the source
tree consumes the local unzipped framework the same way; follow its
binaryTarget shape.
Build the SDK and register the provider
The reference application constructs the session SDK exactly like this:
import RusticMapsSDK
let mapSdk = MapSdk()
try mapSdk.registerMapquestProviders(mapQuestApiKey)
The call throws verbatim when the key is refused.
Pass a key read at runtime (app settings, a secure configuration store,
or a launch environment used by automation). Do not add it to Info.plist
or to an xcconfig that ships in the app; the reference app’s settings sheet
takes it at runtime.
Draw a map surface
The SDK renders onto a Metal-backed UIView you own:
let mapSurface = MapSurfaceView(mapSdk: mapSdk)
// Once the view is laid out and backed by a Metal layer:
let surfaceHandle = MapSurfaceTargetHandle(
appkitViewPointer: nil,
iosViewPointer: unsafeBitCast(metalBackedView, to: UInt64.self),
androidNativeWindowPointer: nil
)
try mapSurface.attach(
surfaceHandle,
widthPixels: UInt32(pixelExtent.width),
heightPixels: UInt32(pixelExtent.height)
)
The reference application drives frames from its own CADisplayLink;
the SDK deliberately has no render loop:
let drawTask = Task { [mapSurface] in
while true {
let camera = currentCamera() // build MapCamera per frame
let statistics = try await mapSurface.draw(camera: camera)
updateReadouts(statistics: statistics)
try await Task.sleep(nanoseconds: frameIntervalNanoseconds)
}
}
Call mapSurface.resize(width:height:) from your layout callback and
mapSurface.detach() when the view leaves the hierarchy. Check
frameSettled(camera:) in the pump to skip a redraw when nothing
changed.
Fit the map to what you show
For a bounded region, let the camera fit the box instead of hand-tuning zoom, exactly as the route-preview flow does:
let fittedCamera = try MapCamera.fitBounds(
boundingBox: regionBox,
viewport: viewport,
edgeInsets: EdgeInsetsPixels(
left: 48, top: 48, right: 48, bottom: 96
),
maximumZoomLevel: 22
)
Before you start
An initialized React Native (bare workflow) application on Android arm64-v8a or iOS, and a downloaded release tarball, and a map-platform API key for the provider you register.
Install the artifact
Install the release tarball from your checked-in copy; no registry is involved:
npm install ./rustic-maps-sdk-rn-v0.1.0.tgz
The package carries the TurboModule glue; link it with your React Native version’s normal autolinking (pod install on the iOS side).
Build the SDK and register the provider
Importing the package entry installs the Rust module into Hermes once, and the generated classes are named exports of it. Construct the SDK and register the provider set:
import { MapSdk } from '@angoratek/rustic-maps-sdk';
export async function createMapSdk(mapQuestApiKey) {
const mapSdk = await new MapSdk();
await mapSdk.registerMapquestProviders(mapQuestApiKey);
return mapSdk;
}
The returned promise rejects verbatim when the key is refused.
Take the key at runtime — a settings screen, secure storage, or a launch environment for automated tests. Do not bake it into the JS bundle or a native build file; the reference app reads it from its settings surface and keeps it only when the user opts in.
Draw a map surface
The React Native artifact exposes the contract surface; the rendered surface lifecycle (native view, attach, per-frame draw) is a native view the host application composes. The reference app shell wires it through its own bridge module and drives the demo from there, which is the recommended starting point for a first integration: read its controller’s lifecycle (create the SDK, register providers, attach the surface, pump camera frames) and copy the sequence, not the demo UI.
Check the installation with the compiled-version probe:
import { getSdkCompiledVersion } from '@angoratek/rustic-maps-sdk';
getSdkCompiledVersion();
// → "0.1.0": the Rust core loaded and answered through the TurboModule.
Where to continue
With the SDK installed, continue in Search and routing; the navigation tutorial’s React Native tab shows the full guided-walk demo flow over this same surface.
Before you start
A Chromium-based browser with WebGPU (flag-free in Chromium since 113; Safari from version 26; Firefox from 141, desktop first) and a downloaded release tarball of the wasm package.
Install the artifact
Unpack the release and serve its directory from any static file server over HTTPS or localhost:
npm install ./rustic-maps-sdk-web-v0.1.0.tgz
cp -r node_modules/@angoratek/rustic-maps-sdk-web ./web
node ./web/server.js # your own static server; localhost is fine
Load the module and start the core
<canvas id="map-canvas"></canvas>
<script type="module">
import {
init,
start_sdk,
} from './web/map_web_bindings.js';
</script>
The page’s entry, exactly as the reference harness does it:
import init, {
MapCamera,
GpuMapCanvas,
CanvasTileResolution,
retinaDevicePixelRatioThreshold,
start_sdk,
get_sdk_compiled_version,
} from './web/map_web_bindings.js';
const compiledVersion = await init().then(() => start_sdk());
console.log(`Maps SDK ready (version ${compiledVersion})`);
The status probe answers the same version string the native surfaces do.
Draw a map surface
The GPU canvas claims the canvas element, acquires the WebGPU device, and renders tiles fetched from a same-origin path you host:
const canvas = document.querySelector('#map-canvas');
const tileResolution = window.devicePixelRatio
>= retinaDevicePixelRatioThreshold()
? CanvasTileResolution.Retina
: CanvasTileResolution.Standard;
const gpuCanvas = new GpuMapCanvas(canvas, tileResolution);
const adapterDescription = await gpuCanvas.initialize();
console.log(`rendering on ${adapterDescription}`);
// The canvas fetches its raster tiles from the same-origin
// /tiles/{zoom}_{tileX}_{tileY}.png path; record or serve imagery there.
// The render frame: build the camera, present, repeat on change.
const fieldOfViewDegrees = 60;
const camera = new MapCamera(47.3769, 8.5417, 14.0, 0.0, 0.0);
const frameStatistics = await gpuCanvas.render_frame(camera, fieldOfViewDegrees);
If initialize() fails in a browser without WebGPU, give the page the
documented degradation message (name navigator.gpu and the missing
capability; link the MDN compatibility table) — never a user-agent
sniff.
Project and unproject
The same camera answers screen mappings without rendering:
const screenPoint = camera.project_to_screen(coordinate, viewport, fieldOfView);
const groundCoordinate = camera.screen_to_ground_coordinate(screenPoint, viewport, fieldOfView);
const tileKeys = camera.visible_tile_keys(viewport, fieldOfView, tileZoom);
The TypeScript types of these value shapes are mirrored in the package’s hand-reviewed type surface; field names are lower camel case.
What you verified
You built the SDK entry object, registered the map provider with your own key, and drew rendered map imagery through the SDK’s renderer. The FFI contract carries every member you called; the generated API reference documents them.
Search and routing
Resolve a typed query into places with the geocoding and search contracts, then plan a route through the results. The tutorial builds on the first map tutorial’s installed SDK; every snippet compiles against the frozen FFI contract.
Resolve a query into places
The provider surface runs on Kotlin coroutines: every MapSdk call you
see here is a suspend fun. Run it from a coroutine scope:
import uniffi.map_native_core.GeographicCoordinate
import uniffi.map_native_core.PlaceResult
suspend fun resolveDestination(mapSdk: MapSdk, queryText: String): PlaceResult? =
mapSdk.forwardGeocode(queryText).firstOrNull()
forwardGeocode answers every candidate the provider resolved, most
likely first; searchAutocomplete answers fast completion candidates
while the user types, and searchPlaces answers full results for a
settled query. Both search calls take an optional BoundingBox boundary
that restricts the search.
Plan the route
calculateRoute takes the ordered waypoints and the routing options. The
reference application plans pedestrian routes like this:
import uniffi.map_native_core.AvoidOptions
import uniffi.map_native_core.RouteOptions
import uniffi.map_native_core.RouteStrategy
import uniffi.map_native_core.TransportationMode
import uniffi.map_native_core.UnitSystem
val routeOptions = RouteOptions(
transportationMode = TransportationMode.PEDESTRIAN,
routeStrategy = RouteStrategy.SHORTEST,
unitSystem = UnitSystem.METRIC,
avoidOptions = AvoidOptions(
tollRoads = false,
limitedAccessHighways = false,
ferries = false,
unpavedRoads = false,
),
departureTimestampSeconds = null,
)
val waypoints = listOf(
originCoordinate,
destinationCoordinate,
)
suspend fun planRoute(mapSdk: MapSdk, waypoints: List<GeographicCoordinate>) =
mapSdk.calculateRoute(waypoints, routeOptions)
The returned RouteData carries total distance and duration, the
geometry’s bounding box, and one leg per waypoint pair, each with its
maneuver steps.
Frame the route preview
Let the camera fit the route’s bounding box through the fitBounds
constructor instead of hand-tuning zoom:
import uniffi.map_native_core.EdgeInsetsPixels
import uniffi.map_native_core.MapCamera
import uniffi.map_native_core.ViewportPixels
val fittedCamera = try {
MapCamera.fitBounds(
route.boundingBox,
ViewportPixels(widthPixels, heightPixels),
EdgeInsetsPixels(left = 224.0, top = 48.0, right = 48.0, bottom = 96.0),
maximumZoomLevel = 24.0,
)
} catch (sdkException: SdkException) {
// A refused fit (no positive box area, unusable insets) surfaces
// verbatim; keep the previous camera.
null
}
The insets you pass are the pixels your overlays keep clear, so the destination never hides under your own panels.
Show the route over the surface
Draw the geometry with the route-polyline overlay (the first map tutorial’s overlay step covers the descriptor), then start a trip on the route in the navigation tutorial.
Resolve a query into places
The provider surface throws on failure and awaits its provider calls:
import RusticMapsSDK
let places = try await mapSdk.forwardGeocode(addressText)
guard let destination = places.first else {
// No candidate: surface the empty state.
return
}
searchAutocomplete gives fast completion candidates for a query under
construction, searchPlaces gives full results for a settled one, and
both take an optional BoundingBox boundary.
Plan the route
let routeOptions = RouteOptions(
transportationMode: TransportationMode.pedestrian,
routeStrategy: RouteStrategy.shortest,
unitSystem: UnitSystem.metric,
avoidOptions: AvoidOptions(
tollRoads: false,
limitedAccessHighways: false,
ferries: false,
unpavedRoads: false
),
departureTimestampSeconds: nil
)
let route = try await mapSdk.calculateRoute(
[originCoordinate, destination.coordinate],
routeOptions: routeOptions
)
try planRoutePreview(route)
The returned RouteData carries distance, duration, the bounding box,
and one leg per waypoint pair with its steps.
Frame and draw the preview
Fit the camera to the route’s bounding box through the insets-aware constructor (shown in the first map tutorial), then hand the route’s geometry to the surface’s overlay list:
let fittedCamera = try MapCamera.fitBounds(
boundingBox: route.boundingBox,
viewport: viewport,
edgeInsets: EdgeInsetsPixels(left: 48, top: 48, right: 48, bottom: 96),
maximumZoomLevel: 22
)
pinnedFitBoundsCamera = fittedCamera
Resolve a query into places
The TurboModule surface mirrors the contract as promise-returning
methods. Everything you call on MapSdk resolves to the same records
the other platforms see, with lower camel case field names:
import { MapSdk, GeographicCoordinate, BoundingBox } from '@angoratek/rustic-maps-sdk';
const mapSdk = new MapSdk();
await mapSdk.registerMapquestProviders(mapQuestApiKey);
// Fast completion candidates while the user types:
const completions = await mapSdk.searchAutocomplete(queryText, null);
// Full results for a settled query, restricted to the visible region:
const results = await mapSdk.searchPlaces(settledQueryText, visibleRegionBoundingBox);
// Address text into a coordinate:
const destination = (await mapSdk.forwardGeocode(destinationText))[0];
Pass null as the boundary to search worldwide, and pass a
BoundingBox (southWest/northEast corners, lower camel case) to
restrict the search.
Plan the route
const waypoints = [
{ latitude: originLatitude, longitude: originLongitude },
{ latitude: destination.coordinate.latitude, longitude: destination.coordinate.longitude },
];
const route = await mapSdk.calculateRoute(waypoints, {
transportationMode: 'Pedestrian',
routeStrategy: 'Shortest',
unitSystem: 'Metric',
avoidOptions: {
tollRoads: false,
limitedAccessHighways: false,
ferries: false,
unpavedRoads: false,
},
departureTimestampSeconds: null,
});
console.log(route.distanceMeters, route.durationSeconds);
A rejected promise carries the typed SdkError; surface it in your
error state instead of retrying blindly (the rate limiter refills on its
own).
What the wasm seam covers today
The WebAssembly surface ships the camera, projection, rendering, and
offline-routing slice of the contract; the async provider surface
(MapSdk’s geocoding, search, and routing members) has no wasm exposure
yet and stays a native-platform surface. The types mirror the contract,
and the contract-sync test keeps the two in lockstep.
Route over an offline graph archive
The wasm seam answers routes over a compiled offline routing archive loaded from in-memory bytes — the same object shape the native routing call returns:
import { offline_route_from_archive_bytes } from './web/map_web_bindings.js';
// The archive, fetched from a same-origin path (the demo serves the
// routing-grid fixture at /offline-graph/routing_grid.rmog):
const archiveBytes = new Uint8Array(
await fetch('/offline-graph/routing_grid.rmog').then(
(archiveResponse) => archiveResponse.arrayBuffer(),
),
);
const route = await offline_route_from_archive_bytes(
archiveBytes,
startLatitude,
startLongitude,
endLatitude,
endLongitude,
);
The route records mirror the contract’s RouteData/RouteLeg/
RouteStep dictionaries in lower camel case (instructionDisplayText,
shape), so a routing UI written for the native surface reads the wasm
answer with no reshaping. Failures throw the verbatim refusal string:
bytes that fail archive validation, a coordinate outside every node’s
snap tolerance, or a request pair the graph cannot connect.
Turn-by-turn navigation
Start a trip on a route, deliver position fixes, and receive the banner instruction, deviation status, phase, and speech-trigger channels. This tutorial rehearses the reference application’s guided-walk demo: the route you planned in Search and routing becomes a simulated drive with guidance. Every snippet compiles against the frozen FFI contract.
Build the controller and subscribe first
Subscribe to the topics before you start the trip, so the initial instruction dispatch of the trip start is received. The listener implements the four callbacks the contract carries:
import uniffi.map_native_core.MapNavigationController
import uniffi.map_native_core.NavigationStateEventListener
class GuidanceListener(
private val onInstruction: (NavigationInstruction) -> Unit,
private val onSpeech: (SpeechInstruction) -> Unit,
) : NavigationStateEventListener {
override fun onNavigationInstruction(instruction: NavigationInstruction) {
onInstruction(instruction)
}
override fun onSpeechInstruction(speechInstruction: SpeechInstruction) {
onSpeech(speechInstruction)
}
override fun onDeviationStatusChange(deviationStatusChange: DeviationStatusChange) {
// Update your reroute affordance here.
}
override fun onNavigationPhaseChange(navigationPhaseChange: NavigationPhaseChange) {
// Drive the navigation drawer's phase state here.
}
}
val navigationController = MapNavigationController()
val listener = GuidanceListener(onInstruction = ::showBanner, onSpeech = ::speak)
val subscriptions = listOf(
navigationController.subscribeToNavigationInstructions(listener),
navigationController.subscribeToSpeechInstructions(listener),
navigationController.subscribeToDeviationStatus(listener),
navigationController.subscribeToPhaseChanges(listener),
)
A subscription is cancelled with its handle when your screen leaves; dropping the reference does not unsubscribe.
Start the trip
Use the SDK’s default policies unless your product measured different ones:
import uniffi.map_native_core.defaultDeviationPolicy
import uniffi.map_native_core.defaultStepAdvancePolicy
import uniffi.map_native_core.UnitSystem
navigationController.startTrip(
route,
UnitSystem.METRIC,
defaultStepAdvancePolicy(),
defaultDeviationPolicy(),
)
The initial instruction payload dispatches before the call returns; the banner shows the first maneuver.
Deliver position fixes
One fix at a time, exactly like a live location source or a recorded trace replay would:
import uniffi.map_native_core.NavigationLocationUpdate
suspend fun replayFixes(
navigationController: MapNavigationController,
samples: Sequence<GeographicCoordinate>,
sampleIntervalMilliseconds: Long,
) {
for (sample in samples) {
navigationController.provideLocationUpdate(
NavigationLocationUpdate(
coordinate = sample,
horizontalAccuracyMeters = 8.0,
courseHeadingDegrees = null,
timestampSeconds = null,
),
)
delay(sampleIntervalMilliseconds)
}
}
Fixes without a reported accuracy never advance a step and are never judged for deviation, so a simulated source must carry its accuracy like a real fix does.
Voice the speech channel
The platform owns playback; the SDK owns when a line is newsworthy.
When onSpeechInstruction delivers a SpeechInstruction, hand its
verbatim text to the platform’s speech engine:
val textToSpeechEngine = TextToSpeech(context, this)
// onSpeechInstruction:
textToSpeechEngine.speak(
speechInstruction.utteranceSpeechText,
TextToSpeech.QUEUE_ADD,
null,
speechInstruction.utteranceId.toString(),
)
The utterance id is the contract’s monotonic id, so your dedup keeps its identity across repeats.
Reroute when guidance freezes
A deviating trip asks for a replacement route through the controller’s gates. Keep the state machine in your own coordinator, exactly as the reference application’s trip driver does:
if (tripState.deviation?.gradeKind == DeviationGradeKind.FULLY_OFF_ROUTE &&
navigationController.beginRerouteCalculation()
) {
coroutineScope.launch {
val replacement = mapSdk.calculateRoute(
listOf(currentFixCoordinate, originalDestination),
routeOptions,
)
navigationController.provideReroute(replacement, defaultStepAdvancePolicy())
}
} else {
navigationController.cancelRerouteCalculation()
}
Build the controller and subscribe first
The same four callbacks arrive as a Swift conforming object:
import RusticMapsSDK
final class GuidanceListener: NavigationStateEventListener {
func onNavigationInstruction(_ instruction: NavigationInstruction) {
// Update the banner instruction.
}
func onSpeechInstruction(_ speechInstruction: SpeechInstruction) {
// Hand the verbatim text to your speech engine.
}
func onDeviationStatusChange(_ deviationStatusChange: DeviationStatusChange) {
// Update the reroute affordance.
}
func onNavigationPhaseChange(_ navigationPhaseChange: NavigationPhaseChange) {
// Drive the phase state.
}
}
let navigationController = MapNavigationController()
let listener = GuidanceListener()
let subscription = navigationController.subscribeToNavigationInstructions(listener)
let speechSubscription = navigationController.subscribeToSpeechInstructions(listener)
Start the trip
try navigationController.startTrip(
route,
unitSystem: UnitSystem.metric,
stepAdvancePolicy: defaultStepAdvancePolicy(),
deviationPolicy: defaultDeviationPolicy()
)
Deliver position fixes
func deliver(_ fix: RecordedFix) {
navigationController.provideLocationUpdate(
NavigationLocationUpdate(
coordinate: fix.coordinate,
horizontalAccuracyMeters: fix.horizontalAccuracyMeters,
courseHeadingDegrees: fix.courseHeadingDegrees,
timestampSeconds: nil
)
)
if let tripState = navigationController.currentTripState() {
updateReadouts(tripState)
}
}
Voice the speech channel
let speechEngine = AVSpeechSynthesizer()
// In onSpeechInstruction:
let utterance = AVSpeechUtterance(string: speechInstruction.utteranceSpeechText)
utterance.voice = AVSpeechSynthesisVoice(language: "en-US")
speechEngine.speak(utterance)
Reroute when guidance freezes
if tripState.deviation?.gradeKind == .fullyOffRoute {
if navigationController.beginRerouteCalculation() {
let replacement = try await mapSdk.calculateRoute(
[currentFixCoordinate, originalDestination],
routeOptions: routeOptions
)
try navigationController.provideReroute(
replacement,
stepAdvancePolicy: defaultStepAdvancePolicy()
)
} else {
navigationController.cancelRerouteCalculation()
}
}
The trip state readback (currentTripState()) carries the phase, the
progress, and isCalculatingNewRoute so your UI can show the
recalculation honestly.
What the React Native surface carries
The TurboModule exposes the same contract members as promise-returning methods, including the navigation controller’s. A guided-walk integration builds a controller object, subscribes the topics, and delivers fixes:
import {
MapNavigationController,
defaultStepAdvancePolicy,
defaultDeviationPolicy,
} from '@angoratek/rustic-maps-sdk';
const navigationController = await new MapNavigationController();
const instructionsSubscription = await navigationController.subscribeToNavigationInstructions(listener);
const speechSubscription = await navigationController.subscribeToSpeechInstructions(listener);
The listener implements the four callback methods the contract carries
(onNavigationInstruction, onSpeechInstruction,
onDeviationStatusChange, onNavigationPhaseChange); each invocation
delivers the same payloads the native platforms receive, with lower
camel case field names.
Start the trip and deliver fixes
await navigationController.startTrip(
route,
'Metric',
defaultStepAdvancePolicy(),
defaultDeviationPolicy(),
);
// One fix per sample of your location source (or its recorded trace):
await navigationController.provideLocationUpdate({
coordinate: { latitude: sampleLatitude, longitude: sampleLongitude },
horizontalAccuracyMeters: 8.0,
courseHeadingDegrees: null,
timestampSeconds: null,
});
const tripState = await navigationController.currentTripState();
Voice the speech channel
The SDK triggers; your application voices. In the speech callback, hand
the verbatim text to the platform’s speech engine (Expo’s Speech, or
your own TTS wrapper), and use the delivery’s utteranceId for dedup:
function onSpeechInstruction(speechInstruction) {
Speech.speak(speechInstruction.utteranceSpeechText, {
language: 'en-US',
onDone: () => markUtteranceComplete(speechInstruction.utteranceId),
});
}
Scope note
The turn-by-turn controller surface is native-platform territory today: the WebAssembly seam ships the camera, rendering, and offline-routing slice, and no navigation controller rides it yet. The contract’s navigation surface stays native (the first map tutorial’s note covers the provider surface the same way).
What runs today
Turn-by-turn state is exercised end to end in the reference applications, and the browser harness runs the same renderer core the navigation follow-camera draws with: the live demo will include a guided walk once its bundle lands. Until then, run the navigation simulation on the native platforms; the demo loop page shows how the browser harness runs and what it exercises.
Offline maps
Keep map imagery and routing working without a network. The SDK’s offline story has two layers: a disk response cache that holds raster tile imagery under your byte budget, and a compiled routing archive that answers routes with no network round trip at all.
Register the providers with a tile cache
One registration call installs the map-platform provider set and wraps the raster-tile contract in the disk response cache. The reference application’s offline-maps demo runs exactly this:
val tileCacheDatabaseFile = File(context.cacheDir, "reference-tile-cache.sqlite")
val tileCacheBudgetBytes = 16777216UL // 16 MiB, the demo's absolute budget
mapSdk.registerMapquestProvidersWithTileCache(
mapQuestApiKey,
tileCacheDatabaseFile.absolutePath,
tileCacheBudgetBytes,
)
The call opens (or creates) the SQLite response cache at the path. A refusal (unusable path, non-positive budget) surfaces verbatim.
Read and tune the caching terms
The cache starts at the tiles category’s default time-to-live, 30 calendar days — the imagery-provider terms ceiling. Tune the live value and read what went into force:
mapSdk.setRasterTileCacheTimeToLive(timeToLiveSeconds)
val terms = mapSdk.rasterTileCacheTerms()
// terms.effectiveTimeToLiveSeconds
// terms.providerTermsCeilingSeconds
// terms.defaultTimeToLiveSeconds
A request above the provider’s declared ceiling is accepted, and the ceiling clamps what the cache retains; the readback shows the effective value, so display it in your offline-maps settings screen.
Pre-warm the tiles you need
Cached tile responses render whenever the network is unavailable: a cache miss goes to the network and back-fills, and a cache hit answers from disk. Render the regions your users expect to revisit once while online, and the cache holds them until the budget or time-to-live says otherwise.
Route without the network
The offline routing layer runs inside the SDK: a compiled routing archive — OpenStreetMap extract in, self-describing archive file out — answers routes with A* over a memory-mapped graph, honoring recorded turn restrictions and deriving maneuver text from the geometry’s bearing change.
The direct route call from foreign code joins the contract as an addition-only member in a later slice; today the offline layer answers the automatic fallback (the routing decorator retries over the offline graph when the online provider fails) and the WebAssembly surface’s offline route seam, and its maneuver texts stay honest where the extract carries no street names.
Register the providers with a tile cache
let tileCacheDatabasePath = FileManager.default
.cachesDirectory.appendingPathComponent("reference-tile-cache.sqlite")
.path
let tileCacheBudgetBytes: UInt64 = 16_777_216 // 16 MiB
try mapSdk.registerMapquestProvidersWithTileCache(
mapQuestApiKey,
tileCacheDatabasePath: tileCacheDatabasePath,
tileCacheAbsoluteMaximumBytes: tileCacheBudgetBytes
)
Read and tune the caching terms
try mapSdk.setRasterTileCacheTimeToLive(timeToLiveSeconds)
let terms = try mapSdk.rasterTileCacheTerms()
// terms.effectiveTimeToLiveSeconds
// terms.providerTermsCeilingSeconds
// terms.defaultTimeToLiveSeconds
Pre-warm and render offline
Cached tiles render whenever the network is unavailable. Render the regions your users revisit while online; the cache answers from disk.
Route without the network
The offline routing layer runs inside the SDK over a compiled archive: A* on a memory-mapped graph, recorded turn restrictions honored, maneuver text derived from the geometry. The direct route call from Swift joins the contract as an addition-only member in a later slice; today the layer serves the automatic fallback when the online provider fails, and the WebAssembly surface’s offline route seam.
Register the providers with a tile cache
import { MapSdk } from '@angoratek/rustic-maps-sdk';
const mapSdk = new MapSdk();
await mapSdk.registerMapquestProvidersWithTileCache(
mapQuestApiKey,
tileCacheDatabasePath, // a writable SQLite path your app owns
16_777_216, // the absolute byte budget (16 MiB)
);
Read and tune the caching terms
await mapSdk.setRasterTileCacheTimeToLive(timeToLiveSeconds);
const terms = await mapSdk.rasterTileCacheTerms();
// terms.effectiveTimeToLiveSeconds
// terms.providerTermsCeilingSeconds
// terms.defaultTimeToLiveSeconds
Show the effective terms in your offline-maps settings screen; a value above the provider’s ceiling is accepted and clamped, and the readback is what went into force.
Scope note
The wasm surface renders from the same-origin /tiles/ imagery path you
host, and loads the offline routing archive from in-memory bytes; the
SQLite tile cache is a native-platform surface (the provider
registration seam has no wasm exposure yet).
Route over the archive
The Search and routing tutorial’s WebAssembly tab shows the full offline routing call: archive bytes in, contract-shaped route records out, no network. The same compiled archive serves the native platforms, so one compiler run feeds every target.
Downloads
First release pending. The artifacts below are published only as GitHub Releases — there is no Maven Central, no npm registry, and no SPM registry. The release workflow that produces this matrix is being rehearsed; when the first tag is published, the links on this page resolve to the released assets, in the exact file names the table lists.
Artifact matrix
| Platform | Artifact file | Status | What it contains |
|---|---|---|---|
| Android | rustic-maps-sdk-android-vX.Y.Z.aar | shipped | Kotlin bindings plus the Rust library for arm64-v8a, armeabi-v7a, x86_64 |
| iOS | rustic-maps-sdk-ios-vX.Y.Z.xcframework.zip | shipped | The XCFramework (arm64 device and simulator slices), packaged for a Swift Package Manager binary target |
| WebAssembly | rustic-maps-sdk-web-vX.Y.Z.tar.gz (raw pkg/) and rustic-maps-sdk-web-vX.Y.Z.tgz (npm) | shipped | The wasm module, its JavaScript shim and both type files; the npm package adds the manifest, licenses and notices |
| React Native | rustic-maps-sdk-rn-vX.Y.Z.tgz (npm, @angoratek/rustic-maps-sdk) | shipped (source package) | The TurboModule package: generated TypeScript, JSI C++, podspec and CMake glue; the native Rust payload lands with the build-rn pipeline item |
| All platforms | rustic-maps-sdk-notices-vX.Y.Z.md | shipped | The third-party dependency notices that accompany your own distribution |
What accompanies every release
Every release carries a per-asset .sha256 sidecar, the aggregated
SHA256SUMS.txt manifest covering all assets, and a Sigstore signature
bundle (.sigstore.json) per asset and for SHA256SUMS.txt itself. The
release is immutable: a problem is fixed by a new tag, never by
replacing an asset.
Verifying an artifact
Verify the checksum first, then the signature. The signature bundles
bind the asset to the release workflow and the vX.Y.Z tag, so the
verification command pins both:
sha256sum --check SHA256SUMS.txt --ignore-missing
cosign verify-blob <artifact-file> \
--bundle <artifact-file>.sigstore.json \
--certificate-identity-regexp \
"https://github.com/angoratek/rustic-maps-sdk/.github/workflows/release.yml@refs/tags/vX.Y.Z" \
--certificate-oidc-issuer https://token.actions.githubusercontent.com \
--yes
The --ignore-missing flag keeps the sha256sum run to the files you
actually downloaded.
Installing from a release
The tutorials carry the per-platform install steps
against these exact artifact names: the checked-in AAR path for Android,
the checksummed zip for the Swift Package Manager binary target, and the
pkg/ archive for the wasm package. The npm packages ship the same
generated trees with a manifest, licenses and notices; the React Native
package is source-only until its native payload joins the release pipeline.
Screenshots
Every screenshot on this page was captured from the reference applications at the same source revision on a standing test device or in the browser loop. Provider credentials were supplied at runtime through each app’s settings surface or its documented automation seam; no build on this page embeds a credential.
Android — Kotlin and Jetpack Compose
iOS — Swift and SwiftUI
WebAssembly — the browser playground
The browser build runs the SDK’s own wasm module and draws through WebGPU, so the playground is the same core rather than a simulation. The capture shows its consumer surface: Plan a trip with a selected demo destination, with the expert controls folded under Advanced. It is served keyless at the live demo over the committed fixture imagery.
React Native — one TypeScript surface, two native shells
Reading the screenshots
The four captures share one camera, one design language (the reference-app UX tokens) and one demo artifact. Differences are the platform’s own chrome: Compose’s Material surface on Android, SwiftUI’s safe-area handling on iOS, the React Native bridge’s host view, and the browser’s WebGPU canvas. The map content, the control cluster, the scale ladder and the attribution line are the same.
Versions and support
This page states how versions are chosen, what a released version line receives, and how changes reach it. The workspace version is 0.1.0, and the first stable release is still being prepared; the Downloads page lists the artifacts when it lands. The Requirements and distributions page carries the per-platform requirement facts.
Version numbering
Versions follow semantic versioning as MAJOR.MINOR.PATCH. One
workspace version is the single source of truth: the Android, iOS, React
Native, and WebAssembly packages are all stamped from it at release
time, so one version string identifies one core.
The SDK is pre-1.0. The FFI contract is addition-only, so existing members keep their signature and meaning across an upgrade. Packaging can still change while the release is 0.x: an artifact-set change or a renamed target bumps the minor version, and a pre-1.0 minor may break a build even when the contract did not change. Treat a minor upgrade as one to test, not one to assume.
Pin exact versions
Release archives are immutable and carry the version in the file name,
for example rustic-maps-sdk-android-vX.Y.Z.aar. Keep the exact version
you validated against and upgrade deliberately. The version you pin also
names the notices file that accompanies it.
Two release tracks
| Track | What it is | What it means for you |
|---|---|---|
| Stable | A vX.Y.Z tag and its release assets | The version to ship; the support statements on this page apply to it |
| Preview | A pre-release tag (vX.Y.Z-rc.N) from the same workflow, marked as a pre-release | Evaluation only; a preview is superseded by the stable release of the same version |
Tags are never moved, and a broken release is superseded by a new tag; the failed release’s assets and checksums stay.
Support window
Each released minor line is intended to receive fixes for a stated window after the next minor line releases. That window length is not yet fixed and is published here with the first stable release. Until it is published, plan to upgrade to the newest minor line for fixes.
Deprecation
The contract’s addition-only rule means a released contract member keeps its signature and meaning; a new release adds members rather than removing them. A deprecation can still mark a member or a packaging detail as superseded, and it is announced in the release notes and the changelog before the release that retires it. The exact deprecation lead time is not yet fixed and is published here with the first stable release.
Checking the loaded version
The compiled SDK answers get_sdk_compiled_version on every surface.
Compare it with the artifact you shipped. A mismatch means the loaded
core and your dependency declaration disagree, and a diagnostics screen
should show the value.
Getting support
Report a problem through the project’s issue tracker with four things:
the get_sdk_compiled_version output, your platform and operating
system version, the exact SDK version you pinned, and a minimal
reproduction. For a rendering problem, include the TileFrameStatistics
of an affected frame. For a provider problem, include the SdkError
variant and its verbatim detail.
Changelog
The SDK is in active development; everything below is unreleased. The FFI contract surface it describes changes addition-only: existing members keep their signature and meaning. This page is a curated, public-facing summary. The complete development log lives in the source repository.
Imagery and caching
- All seven MapQuest provider APIs are implemented and fixture-tested. They cover routes with strategies and multiple legs, the route matrix, forward and reverse geocoding, search prediction, traffic incidents, traffic-flow congestion polylines, and raster tiles in standard and retina resolutions.
- A SQLite-backed response cache holds provider responses under an
absolute byte budget, a share of free disk space, or both. It provides
per-category time-to-live, LRU eviction, and cache-then-network
behavior around every provider contract. Tile imagery caches heavily
by default: 30 calendar days, the common maximum under
imagery-provider terms. Each provider contract declares its own caching
terms, so an integrator tunes the cache knowingly per provider
(
setRasterTileCacheTimeToLive, read back throughrasterTileCacheTerms). Values above a provider’s declared ceiling stay compliance-clamped.
Rendering
- The project maintains its own minimal
wgpuraster renderer. It implements web-mercator projection with exact frustum tile listing (Sutherland–Hodgman over the six frustum planes, with antimeridian world-copy handling), an anchored camera with fit-bounds and insets-aware framing and aflyTo-style hyperbolic arc, a byte-bounded GPU texture cache, and a tile scheduler with per-frame dedup, parent fallback, and a fetch budget. Route, polygon, marker, and traffic overlay layers sit on top of the tiles, and the renderer owns the location indicator (a tracking dot with accuracy ring, or a guided course arrow). - The same renderer scene draws headless (deterministic golden-image tests), into live Android/iOS surfaces, and in the browser through WebGPU. One WGSL/vertex pattern is byte-identical across the wasm and native paths.
Offline routing
- The offline routing pipeline is in place. A deterministic compiler turns an OpenStreetMap PBF extract into a self-describing routing archive (nodes, a per-edge pack with road class, speed, and access columns, and turn restrictions). A memory-mapped reader validates the container’s alignment invariants. An offline routing provider answers routes with A* over the graph. It honors recorded turn restrictions and derives turn maneuvers (left/right/slight/sharp/U-turn) from the geometry’s bearing change; its texts stay honest where the archive carries no street names. A routing decorator retries over the offline graph when the primary (online) provider fails.
- The same graph runs in the browser: the wasm surface loads the compiled archive from in-memory bytes and answers the same route-shaped objects and typed refusals, so offline routing behavior is regression-tested in a plain browser run.
Navigation
- Navigation state is a pure reducer over
Option<NavigationSession>. It implements Ferrostar-pattern step-advance conditions (manual, eager distance-to-end, delayed current-step, Or/And combinators, and the fresh-instance reset contract), deviation detection with accuracy-aware allowances, and provider-verbatim instruction composition (banner and speech text; nothing rewritten). - Deviation is graded. A fully-off-route state freezes the guidance and asks for a reroute; an off-step-but-on-route state keeps counting down and reconciles to the projected step. Speed runs advance through many steps per fix, and the advance is bounded and stack-safety-tested. The fully-off-route instruction text suppresses the countdown honestly rather than projecting it from a stale anchor.
- The SDK owns speech triggering: it publishes a speech-instruction topic over documented distance bands. Platforms voice that topic and keep their own pacing and focus policies.
Camera and gestures
- The gesture-input camera seam keeps the camera math in the SDK. Platform recognizers forward plain deltas, and the SDK applies them: pan by screen pixels, focal-point zoom and rotation with an exact hold-the-focal-point correction, clamped through the camera’s own ranges.
Bindings and platforms
- UniFFI drives the generated Kotlin/Swift bindings and the React Native bindings from a frozen, addition-only UDL contract. The wasm surface is a hand-reviewed TypeScript mirror regression-checked against the same UDL. Callback providers (geocoding, tiles) and a navigation controller with per-topic subscriptions cross the foreign callbacks seam. Exported async methods run through a crate-owned runtime handback, so foreign event loops never meet an unserved reactor.
- Reference applications on Android (Compose), iOS (SwiftUI), and the web playground implement one shared design language. Each reaches search → route → simulated turn-by-turn with speech through a bottom-sheet home, a five-control cluster (zoom, scale bar, my-location, 2D/3D, compass), a follow camera, an offline tile-cache card, and a readiness-chip diagnostics readout. The web surface reaches reference-app parity with the native demos.
Vector tiles
- A Mapbox Vector Tile v2.1 decoder turns fetched tiles into layers, features and geometries with typed errors on every malformed path. A data-only style pass maps decoded layers into the renderer’s fill, polyline and point overlays (an optional one-attribute text filter is supported). The OpenStreetMap US Sourdough tileset is the first vector-tile provider, with attribution and terms carried by its source descriptor.
Provider credentials
- The reference applications never embed a provider key in a build. They start keyless and take credentials at runtime from a settings surface whose rows come from one shared provider registry; keys stay in memory unless the user opts in to device secure storage (Keychain or Android Keystore). Automated UI runs can inject a credential through a launch intent/environment; it is never persisted or logged.
Releases and the live demo
- A tag-driven release pipeline runs the gate set on the tagged tree, builds the Android AAR, the iOS XCFramework archive and the wasm package, and assembles a draft GitHub Release with an exact asset-set check, SHA-256 sidecars and manifest, keyless Sigstore signature bundles, regenerated third-party notices and changelog-derived notes.
- The same WebAssembly build powers a live, keyless demo at this site’s
/demopath. Its provider panel can take a runtime key for live geocoding, routing and tile probes; the static demo defaults to the recorded fixture data.
Diagnostics and ops
- A
tracing-facade diagnostics layer provides bounded, per-subsystem ring sinks with coordinate scrubbing at the sink. Its severity gate never raises once finer events have been requested. Subscribe and flush seams on the native and wasm surfaces back the reference apps’ readouts and a later log-sharing story. - CI runs fmt, clippy with warnings-as-errors, the workspace test suite, a wasm-targeted clippy closure, a browser e2e suite, and the documentation-site pipeline (build → scrub → stage) on every push.
Attribution
Each provider descriptor carries its own attribution line, and the reference applications render it from the shared registry. The bundled demo currently shows MapQuest imagery and OpenStreetMap data: map imagery © MapQuest; map data © OpenStreetMap contributors (ODbL). Third-party dependency notices are regenerated into their chapter.
FAQ and troubleshooting
The caveats integrators actually hit, collected from the code and the reference applications. The performance page carries the tuning guidance; this page carries what to check when something misbehaves.
Web GPU and browser support
The browser surface needs WebGPU; there is no WebGL fallback. The
wasm canvas fails its initialize() when no WebGPU adapter supports the
canvas or the device request fails, and then reports the error verbatim.
Your page should catch it and present the degradation message: name the
missing capability (navigator.gpu is the feature test), and link
MDN’s compatibility table. Version floor: Chromium-based browsers since
113 (flag-free), Safari since 26, Firefox since 141 (desktop first), and
always a secure context (HTTPS or localhost). No user-agent sniffing.
The map stays empty on an Android emulator after rendering starts.
Android emulators render through SwiftShader over ANGLE, a software
graphics stack; that is an expected emulator fact, not a failure. On the
emulator the GPU surface negotiates through the GL backend: set
WGPU_BACKEND=gl in your build’s environment as the reference
application does. On real hardware the line is redundant and harmless.
API key and provider failures
How do I supply the provider key? Read it at runtime — from your app’s own secure configuration or a settings surface — and pass it to the provider registration call. Never commit it or bake it into an app bundle. The reference applications ship keyless and take the key from their settings view, with opt-in secure-device storage; their UI suites inject one through a launch intent/environment for automated runs.
The SDK throws immediately at registration when the key is refused.
registerMapquestProviders answers with the provider’s verbatim detail;
surface it, do not catch-and-continue.
Calls throw RateLimitExhausted. The shared token-bucket limiter is
exhausted. The bucket refills on its own schedule, so retry after the
next refill instead of surfacing an error; cache-then-network behavior
around every provider contract keeps repeated identical requests free of
quota.
Calls throw ProviderNotConfigured naming a contract. That contract
has no registered implementation on the MapSdk object. Registration is
per object: re-registration replaces every slot, so a second
registerMapquestProviders call is safe, but a second MapSdk() needs
its own registration.
Elevation throws ProviderNotConfigured even with the provider set
installed. The elevation contract has no provider implementation yet;
its calls keep refusing until one lands.
Offline behavior
Tiles render but routes fail offline, or the other way around. The
two offline layers are separate. Tile imagery answers from the disk
cache you registered (registerMapquestProvidersWithTileCache), within
its byte budget and time-to-live. Routing answers from a compiled
routing archive, independent of the imagery cache; check that the
archive you compiled covers the queried region (see the next item).
A route refusal comes back offline. The offline graph refuses, rather than guessing, in three cases: bytes that fail the archive validation, a coordinate outside every node’s snap tolerance, and a request pair the graph cannot connect. Check the archive covers the areas you query; a small extract is the usual cause.
Maneuver text has no street names offline. The archived graph carries no street-name strings, so the offline maneuver texts name the turn direction and the distance and stay honest about what they do not know. Named maneuver text arrives from the online provider.
Tile cache
A tuned time-to-live does not stick after re-registration.
Registration is a reset: a freshly registered cache starts again at the
category default, so re-apply the override after registering. The
rasterTileCacheTerms readback shows the effective value.
A large requested time-to-live is accepted but the cache still
expires entries. The installed provider’s declared terms ceiling
(compliance-bound at 30 calendar days for the imagery) clamps what the
cache retains. Read the ceiling from
rasterTileCacheTerms and display it next to the effective value.
Setting a zero time-to-live throws. A zero value would expire every entry on capture; the refusal names the mistake.
Compliance
Attribution. Keep the attribution line visible in your application: imagery © MapQuest; map data © OpenStreetMap contributors (ODbL). If you redistribute the SDK or ship an application built on it, reproduce the third-party notices with your distribution.
Third-Party Notices
Generated by scripts/generate-third-party-notices from the workspace dependency graph (Cargo.toml manifests + Cargo.lock, via cargo metadata --offline — no network access). License identities are additionally gated by cargo-deny (deny.toml). Regenerate and commit this file whenever any dependency changes. Output is deterministic, so a re-run on an unchanged graph is a byte-identical no-op.
This file lists the third-party software the workspace depends on: 384 registry packages (326 reachable through normal dependency edges, 58 test/dev-only). The first-party workspace crates are not listed here; they carry the SDK’s own license (MIT OR Apache-2.0, see Cargo.toml).
Map-data attribution
Separate from the software above: map imagery © MapQuest; map data © OpenStreetMap contributors, licensed under the Open Database License (ODbL). Every reference app renders this attribution in-app from the shared provider registry descriptors (platform/providers.json; the web playground reads the same file). Integrators must keep this attribution visible in their own apps and reproduce this notices file with their distribution.
License summary
| SPDX license expression | Packages |
|---|---|
(MIT OR Apache-2.0) AND Unicode-3.0 | 1 |
0BSD OR MIT OR Apache-2.0 | 1 |
Apache-2.0 | 10 |
Apache-2.0 / MIT | 1 |
Apache-2.0 AND ISC | 1 |
Apache-2.0 OR BSL-1.0 | 1 |
Apache-2.0 OR ISC OR MIT | 2 |
Apache-2.0 OR MIT | 22 |
Apache-2.0 WITH LLVM-exception OR Apache-2.0 OR MIT | 5 |
Apache-2.0/MIT | 3 |
BSD-2-Clause OR Apache-2.0 OR MIT | 2 |
BSD-3-Clause | 1 |
BSD-3-Clause OR Apache-2.0 | 2 |
CDLA-Permissive-2.0 | 1 |
ISC | 3 |
MIT | 62 |
MIT OR Apache-2.0 | 196 |
MIT OR Apache-2.0 OR LGPL-2.1-or-later | 1 |
MIT OR Apache-2.0 OR Zlib | 3 |
MIT OR Zlib OR Apache-2.0 | 2 |
MIT/Apache-2.0 | 17 |
MPL-2.0 | 9 |
Unicode-3.0 | 18 |
Unlicense OR MIT | 6 |
Unlicense/MIT | 2 |
Zlib | 3 |
Zlib OR Apache-2.0 OR MIT | 9 |
| Total | 384 |
Packages by license
(MIT OR Apache-2.0) AND Unicode-3.0 (1 package(s))
| Package | Version | Homepage |
|---|---|---|
unicode-ident | 1.0.27 | https://github.com/dtolnay/unicode-ident |
0BSD OR MIT OR Apache-2.0 (1 package(s))
| Package | Version | Homepage |
|---|---|---|
adler2 | 2.0.1 | https://github.com/oyvindln/adler2 |
Apache-2.0 (10 package(s))
| Package | Version | Homepage |
|---|---|---|
borsh-derive† | 1.8.1 | https://borsh.io |
ciborium† | 0.2.2 | https://github.com/enarx/ciborium |
ciborium-io† | 0.2.2 | https://github.com/enarx/ciborium |
ciborium-ll† | 0.2.2 | https://github.com/enarx/ciborium |
codespan-reporting | 0.13.1 | https://github.com/brendanzab/codespan |
gl_generator† | 0.14.0 | https://github.com/brendanzab/gl-rs/ |
glutin_wgl_sys | 0.6.1 | https://github.com/rust-windowing/glutin |
khronos_api† | 3.1.0 | https://github.com/brendanzab/gl-rs/ |
spirv | 0.4.0+sdk-1.4.341.0 | https://github.com/gfx-rs/rspirv |
sync_wrapper | 1.0.2 | https://docs.rs/sync_wrapper |
† development/test-only dependency: compiled inside this repository’s own test harnesses, but not linked into a published SDK artifact.
Apache-2.0 / MIT (1 package(s))
| Package | Version | Homepage |
|---|---|---|
fnv | 1.0.7 | https://github.com/servo/rust-fnv |
Apache-2.0 AND ISC (1 package(s))
| Package | Version | Homepage |
|---|---|---|
ring | 0.17.14 | https://github.com/briansmith/ring |
Apache-2.0 OR BSL-1.0 (1 package(s))
| Package | Version | Homepage |
|---|---|---|
ryu | 1.0.23 | https://github.com/dtolnay/ryu |
Apache-2.0 OR ISC OR MIT (2 package(s))
| Package | Version | Homepage |
|---|---|---|
hyper-rustls | 0.27.10 | https://github.com/rustls/hyper-rustls |
rustls | 0.23.46 | https://github.com/rustls/rustls |
Apache-2.0 OR MIT (22 package(s))
† development/test-only dependency: compiled inside this repository’s own test harnesses, but not linked into a published SDK artifact.
Apache-2.0 WITH LLVM-exception OR Apache-2.0 OR MIT (5 package(s))
| Package | Version | Homepage |
|---|---|---|
linux-raw-sys | 0.12.1 | https://github.com/sunfishcode/linux-raw-sys |
linux-raw-sys† | 0.4.15 | https://github.com/sunfishcode/linux-raw-sys |
rustix† | 0.38.44 | https://github.com/bytecodealliance/rustix |
rustix | 1.1.6 | https://github.com/bytecodealliance/rustix |
wasi | 0.11.1+wasi-snapshot-preview1 | https://github.com/bytecodealliance/wasi |
† development/test-only dependency: compiled inside this repository’s own test harnesses, but not linked into a published SDK artifact.
Apache-2.0/MIT (3 package(s))
| Package | Version | Homepage |
|---|---|---|
console_error_panic_hook | 0.1.7 | https://github.com/rustwasm/console_error_panic_hook |
pollster | 0.4.0 | https://github.com/zesterer/pollster |
rustc-hash | 1.1.0 | https://github.com/rust-lang-nursery/rustc-hash |
BSD-2-Clause OR Apache-2.0 OR MIT (2 package(s))
| Package | Version | Homepage |
|---|---|---|
zerocopy | 0.8.63 | https://github.com/google/zerocopy |
zerocopy-derive | 0.8.63 | https://github.com/google/zerocopy |
BSD-3-Clause (1 package(s))
| Package | Version | Homepage |
|---|---|---|
subtle | 2.6.1 | https://dalek.rs/ |
BSD-3-Clause OR Apache-2.0 (2 package(s))
| Package | Version | Homepage |
|---|---|---|
moxcms | 0.8.1 | https://github.com/awxkee/moxcms |
pxfm | 0.1.30 | https://github.com/awxkee/pxfm |
CDLA-Permissive-2.0 (1 package(s))
| Package | Version | Homepage |
|---|---|---|
webpki-roots | 1.0.11 | https://github.com/rustls/webpki-roots |
This is a data license, not a software license: it covers the root-certificate bundle published by webpki-roots, not code. It permits use, sharing and commercial use with attribution. Full text: https://spdx.org/licenses/CDLA-Permissive-2.0.txt.
ISC (3 package(s))
| Package | Version | Homepage |
|---|---|---|
libloading | 0.8.9 | https://github.com/nagisa/rust_libloading/ |
rustls-webpki | 0.103.16 | https://github.com/rustls/webpki |
untrusted | 0.9.0 | https://github.com/briansmith/untrusted |
MIT (62 package(s))
† development/test-only dependency: compiled inside this repository’s own test harnesses, but not linked into a published SDK artifact.
MIT OR Apache-2.0 (196 package(s))
† development/test-only dependency: compiled inside this repository’s own test harnesses, but not linked into a published SDK artifact.
MIT OR Apache-2.0 OR LGPL-2.1-or-later (1 package(s))
| Package | Version | Homepage |
|---|---|---|
r-efi | 6.0.0 | https://github.com/r-efi/r-efi/wiki |
These packages offer LGPL-2.1-or-later only as one alternative inside a permissive SPDX expression; this SDK relies on the permissive alternatives. Full text: https://spdx.org/licenses/LGPL-2.1-or-later.txt.
MIT OR Apache-2.0 OR Zlib (3 package(s))
| Package | Version | Homepage |
|---|---|---|
glow | 0.17.0 | https://github.com/grovesNL/glow.git |
lru-slab | 0.1.3 | https://github.com/Ralith/lru-slab |
raw-window-handle | 0.6.2 | https://github.com/rust-windowing/raw-window-handle |
MIT OR Zlib OR Apache-2.0 (2 package(s))
| Package | Version | Homepage |
|---|---|---|
miniz_oxide | 0.8.9 | https://github.com/Frommi/miniz_oxide/tree/master/miniz_oxide |
miniz_oxide | 0.9.1 | https://github.com/Frommi/miniz_oxide/tree/master/miniz_oxide |
MIT/Apache-2.0 (17 package(s))
† development/test-only dependency: compiled inside this repository’s own test harnesses, but not linked into a published SDK artifact.
MPL-2.0 (9 package(s))
| Package | Version | Homepage |
|---|---|---|
uniffi | 0.32.2 | https://mozilla.github.io/uniffi-rs |
uniffi_bindgen | 0.32.2 | https://mozilla.github.io/uniffi-rs |
uniffi_build | 0.32.2 | https://mozilla.github.io/uniffi-rs |
uniffi_core | 0.32.2 | https://mozilla.github.io/uniffi-rs |
uniffi_internal_macros | 0.32.2 | https://mozilla.github.io/uniffi-rs |
uniffi_macros | 0.32.2 | https://mozilla.github.io/uniffi-rs |
uniffi_meta | 0.32.2 | https://mozilla.github.io/uniffi-rs |
uniffi_pipeline | 0.32.2 | https://mozilla.github.io/uniffi-rs |
uniffi_udl | 0.32.2 | https://mozilla.github.io/uniffi-rs |
The Mozilla Public License 2.0 applies only to the source files of the covered crates (file-level copyleft — not viral). This SDK consumes those crates as libraries without modifying their sources. Full text: https://spdx.org/licenses/MPL-2.0.txt. Note: the published uniffi crate family ships no license text file inside its package; the full text lives in each crate’s repository.
Unicode-3.0 (18 package(s))
| Package | Version | Homepage |
|---|---|---|
icu_collections | 2.4.0 | https://icu4x.unicode.org |
icu_locale_core | 2.4.0 | https://icu4x.unicode.org |
icu_normalizer | 2.4.0 | https://icu4x.unicode.org |
icu_normalizer_data | 2.4.0 | https://icu4x.unicode.org |
icu_properties | 2.4.0 | https://icu4x.unicode.org |
icu_properties_data | 2.4.0 | https://icu4x.unicode.org |
icu_provider | 2.4.0 | https://icu4x.unicode.org |
litemap | 0.8.3 | https://github.com/unicode-org/icu4x |
potential_utf | 0.1.6 | https://icu4x.unicode.org |
tinystr | 0.8.5 | https://github.com/unicode-org/icu4x |
writeable | 0.6.4 | https://github.com/unicode-org/icu4x |
yoke | 0.8.4 | https://github.com/unicode-org/icu4x |
yoke-derive | 0.8.4 | https://github.com/unicode-org/icu4x |
zerofrom | 0.1.8 | https://github.com/unicode-org/icu4x |
zerofrom-derive | 0.1.8 | https://github.com/unicode-org/icu4x |
zerotrie | 0.2.5 | https://icu4x.unicode.org |
zerovec | 0.11.9 | https://github.com/unicode-org/icu4x |
zerovec-derive | 0.11.7 | https://github.com/unicode-org/icu4x |
Unlicense OR MIT (6 package(s))
| Package | Version | Homepage |
|---|---|---|
aho-corasick† | 1.1.5 | https://github.com/BurntSushi/aho-corasick |
byteorder | 1.5.0 | https://github.com/BurntSushi/byteorder |
byteorder-lite | 0.1.0 | https://github.com/image-rs/byteorder-lite |
memchr | 2.8.3 | https://github.com/BurntSushi/memchr |
termcolor | 1.4.1 | https://github.com/BurntSushi/termcolor |
winapi-util | 0.1.11 | https://github.com/BurntSushi/winapi-util |
† development/test-only dependency: compiled inside this repository’s own test harnesses, but not linked into a published SDK artifact.
Unlicense/MIT (2 package(s))
| Package | Version | Homepage |
|---|---|---|
same-file† | 1.0.6 | https://github.com/BurntSushi/same-file |
walkdir† | 2.5.0 | https://github.com/BurntSushi/walkdir |
† development/test-only dependency: compiled inside this repository’s own test harnesses, but not linked into a published SDK artifact.
Zlib (3 package(s))
| Package | Version | Homepage |
|---|---|---|
foldhash | 0.2.0 | https://github.com/orlp/foldhash |
slotmap | 1.1.1 | https://github.com/orlp/slotmap |
zlib-rs | 0.6.8 | https://github.com/trifectatechfoundation/zlib-rs |
Zlib OR Apache-2.0 OR MIT (9 package(s))
| Package | Version | Homepage |
|---|---|---|
bytemuck | 1.25.2 | https://github.com/Lokathor/bytemuck |
bytemuck_derive | 1.12.1 | https://github.com/Lokathor/bytemuck |
dispatch2 | 0.3.1 | https://github.com/madsmtm/objc2 |
objc2-core-foundation | 0.3.2 | https://github.com/madsmtm/objc2 |
objc2-core-graphics | 0.3.2 | https://github.com/madsmtm/objc2 |
objc2-io-surface | 0.3.2 | https://github.com/madsmtm/objc2 |
objc2-metal | 0.3.2 | https://github.com/madsmtm/objc2 |
objc2-quartz-core | 0.3.2 | https://github.com/madsmtm/objc2 |
tinyvec | 1.13.3 | https://github.com/Lokathor/tinyvec |
Copyright notice lines (extracted from vendored crates)
First attribution lines extracted from the license files vendored in the local cargo cache; each crate’s source carries the complete text. Crates without an extractable line (or absent from the local cache) simply appear only in the license tables above, which remain authoritative.
unicode-ident1.0.27 — Copyright © 1991-2023 Unicode, Inc.borsh-derive1.8.1 — Copyright 2019 Nearglutin_wgl_sys0.6.1 — Copyright 2022 Kirill Chibisovfnv1.0.7 — Copyright (c) 2017 Contributorsring0.17.14 — Copyright (c) 2009 The Go Authors. All rights reserved. / Copyright 2015 The Chromium Authors. All rights reserved. / Copyright 2015-2025 Brian Smith.hyper-rustls0.27.10 — Copyright (c) 2016, Joseph Birr-Pixton jpixton@gmail.com / Copyright (c) 2016 Joseph Birr-Pixton jpixton@gmail.comrustls0.23.46 — Copyright (c) 2016, Joseph Birr-Pixton jpixton@gmail.com / Copyright (c) 2016 Joseph Birr-Pixton jpixton@gmail.comatomic-waker1.1.2 — Copyright (c) 2016 Alex Crichton / Copyright (c) 2017 The Tokio Authorsautocfg1.5.1 — Copyright (c) 2018 Josh Stonebit-set0.10.0 — Copyright (c) 2026 The Rust Project Developersbit-set0.11.1 — Copyright (c) 2023 The Rust Project Developersbit-vec0.10.1 — Copyright (c) 2023 The Rust Project Developersbit-vec0.9.1 — Copyright (c) 2023 The Rust Project Developersconst-oid0.10.2 — Copyright (c) 2020-2026 The RustCrypto Project Developerscriterion0.5.1 — Copyright (c) 2014 Jorge Aparicioequivalent1.0.3 — Copyright (c) 2016–2023indexmap2.14.2 — Copyright (c) 2016–2017tinytemplate1.2.1 — Copyright (c) 2019 Brook Heisleruuid1.29.0 — Copyright (c) 2014 The Rust Project Developers / Copyright (c) 2018 Ashley Mannix, Christopher Armstrong, Dylan DPC, Hunar Roop Kahlonzeroize1.9.1 — Copyright (c) 2018-2026 The RustCrypto Project Developersconsole_error_panic_hook0.1.7 — Copyright (c) 2018 Nick Fitzgeraldpollster0.4.0 — Copyright (c) 2020-2021 Joshua Barrettozerocopy0.8.63 — Copyright 2023 The Fuchsia Authors / Copyright 2019 The Fuchsia Authors.zerocopy-derive0.8.63 — Copyright 2023 The Fuchsia Authors / Copyright 2019 The Fuchsia Authors.subtle2.6.1 — Copyright (c) 2016-2017 Isis Agora Lovecruft, Henry de Valence. All rights reserved. / Copyright (c) 2016-2024 Isis Agora Lovecruft. All rights reserved.moxcms0.8.1 — Copyright 2024 Radzivon Bartoshykpxfm0.1.30 — Copyright 2024 Radzivon Bartoshyklibloading0.8.9 — Copyright © 2015, Simonas Kazlauskasrustls-webpki0.103.16 — Copyright 2015 Brian Smith.untrusted0.9.0 — // Copyright 2015-2016 Brian Smith.assert-json-diff2.0.2 — Copyright (c) 2019 David Pedersenbytes1.12.1 — Copyright (c) 2018 Carl Lerchecfg_aliases0.2.2 — Copyright (c) 2020 Katharos Technologycrunchy0.2.4 — Copyright 2017-2023 Eira Fransham.dlib0.5.3 — Copyright (c) 2015 Victor Bergerh20.4.20 — Copyright (c) 2017 h2 authorshttp-body1.1.0 — Copyright (c) 2019-2026 Sean McArthur & Hyper Contributorshttp-body-util0.1.5 — Copyright (c) 2019-2026 Sean McArthur & Hyper Contributorshyper1.12.0 — Copyright (c) 2014-2026 Sean McArthurhyper-util0.1.21 — Copyright (c) 2023-2025 Sean McArthuris-terminal0.4.17 — Copyright (c) 2015-2019 Doug Tangrenlibm0.2.16 — Copyright (c) 2018 Jorge Aparicio / Copyright © 2005-2020 Rich Felker, et al. / Copyright © 1993,2004 Sun Microsystems orlibsqlite3-sys0.38.2 — Copyright (c) 2014 The rusqlite developersmio1.2.4 — Copyright (c) 2014 Carl Lerche and other MIO contributorsnom7.1.3 — Copyright (c) 2014-2019 Geoffroy Couprieoorandom11.1.5 — Copyright (c) 2019 Simon Heathordered-float5.5.1 — Copyright (c) 2015 Jonathan Reemprotobuf3.7.2 — Copyright (c) 2019 Stepan Koltsovprotobuf-codegen3.7.2 — Copyright (c) 2019 Stepan Koltsovprotobuf-parse3.7.2 — Copyright (c) 2019 Stepan Koltsov / Copyright 2008 Google Inc. All rights reserved.protobuf-support3.7.2 — Copyright (c) 2019 Stepan Koltsovr2d2_sqlite0.35.0 — Copyright (c) 2015 Jovansonlee Cesarredox_syscall0.5.18 — Copyright (c) 2017 Redox OS Developersrusqlite0.40.2 — Copyright (c) 2014 The rusqlite developersscroll_derive0.12.1 — Copyright (c) 2017slab0.4.12 — Copyright (c) 2019 Carl Lerchesmawk0.3.3 — Copyright (c) 2017 Martin Geislersqlite-wasm-rs0.5.5 — Copyright (c) 2024 Spxgstrsim0.11.1 — Copyright (c) 2015 Danny Guo / Copyright (c) 2016 Titus Wormer tituswormer@gmail.com / Copyright (c) 2018 Akash Kurdekarsynstructure0.14.0 — Copyright 2016 Nika Layzelltextwrap0.16.4 — Copyright (c) 2016 Martin Geislertokio-macros2.7.2 — Copyright (c) 2019 Yoshua Wuytstower0.5.3 — Copyright (c) 2019 Tower Contributorstower-http0.6.11 — Copyright (c) 2019-2021 Tower Contributorstower-layer0.3.3 — Copyright (c) 2019 Tower Contributorstower-service0.3.3 — Copyright (c) 2019 Tower Contributorstracing0.1.44 — Copyright (c) 2019 Tokio Contributorstracing-attributes0.1.31 — Copyright (c) 2019 Tokio Contributorstracing-core0.1.36 — Copyright (c) 2019 Tokio Contributorstry-lock0.2.5 — Copyright (c) 2018-2023 Sean McArthur / Copyright (c) 2016 Alex Crichtonwant0.3.2 — Copyright (c) 2018-2019 Sean McArthurwayland-sys0.31.11 — Copyright (c) 2015 Elinor Bergerweedle25.0.0 — Copyright 2018-Present Sharad Chand / Copyright 2022 Jan-Erik Redigerwhich4.4.2 — Copyright (c) 2015 fangyuanzitixml-rs0.8.29 — Copyright (c) 2014 Vladimir Matveevandroid_system_properties0.1.6 — Copyright 2016 Nicolas Silva / Copyright (c) 2013 Nicolas Silvaash0.38.0+1.3.281 — Copyright 2016 Maik Klein / Copyright (c) 2016 ASHaskama0.16.1 — Copyright 2017-2020 Dirkjan Ochtman / Copyright (c) 2017-2020 Dirkjan Ochtmanaskama_derive0.16.1 — Copyright 2017-2020 Dirkjan Ochtman / Copyright (c) 2017-2020 Dirkjan Ochtmanaskama_macros0.16.1 — Copyright 2017-2020 Dirkjan Ochtman / Copyright (c) 2017-2020 Dirkjan Ochtmanaskama_parser0.16.1 — Copyright 2017-2020 Dirkjan Ochtman / Copyright (c) 2017-2020 Dirkjan Ochtmanbase640.22.1 — Copyright (c) 2015 Alice Mazbase640.23.1 — Copyright (c) 2025 Alice Maz, Marshall Piercebitflags2.13.2 — Copyright (c) 2014 The Rust Project Developersblock-buffer0.12.1 — Copyright (c) 2018-2025 The RustCrypto Project Developersborsh1.8.1 — Copyright 2019 Nearbumpalo3.20.3 — Copyright (c) 2019 Nick Fitzgeraldcast0.3.0 — Copyright (c) 2014-2017 Jorge Apariciocc1.10.0 — Copyright (c) 2014 Alex Crichtoncfg-if1.0.5 — Copyright (c) 2014 Alex Crichtonchacha200.10.2 — Copyright (c) 2019-2026 The RustCrypto Project Developerscpufeatures0.3.1 — Copyright (c) 2020-2026 The RustCrypto Project Developerscrc32fast1.5.3 — Copyright (c) 2018 Sam Rijs, Alex Crichton and contributorscrossbeam-deque0.8.8 — Copyright (c) 2019 The Crossbeam Project Developerscrossbeam-epoch0.9.21 — Copyright (c) 2019 The Crossbeam Project Developerscrossbeam-utils0.8.23 — Copyright (c) 2019 The Crossbeam Project Developerscrypto-common0.2.2 — Copyright (c) 2021-2026 RustCrypto Developersdeadpool0.12.3 — Copyright 2019 Michael P. Jung / Copyright (c) 2019 Michael P. Jungdeadpool-runtime0.1.4 — Copyright 2019 Michael P. Jung / Copyright (c) 2019 Michael P. Jungdigest0.11.3 — Copyright (c) 2017-2025 RustCrypto Developers / Copyright (c) 2017 Artyom Pavlovdocument-features0.2.12 — Copyright (c) 2020 Olivier Goffart ogoffart@sixtyfps.ioeither1.19.0 — Copyright (c) 2015errno0.3.14 — Copyright (c) 2014 Chris Wongfind-msvc-tools0.1.15 — Copyright (c) 2014 Alex Crichtonflate21.1.10 — Copyright (c) 2014-2026 Alex Crichtonform_urlencoded1.2.2 — Copyright (c) 2013-2016 The rust-url developersfs41.1.0 — Copyright (c) 2015 The Rust Project Developersfutures0.3.34 — Copyright (c) 2016 Alex Crichton / Copyright (c) 2017 The Tokio Authorsfutures-channel0.3.34 — Copyright (c) 2016 Alex Crichton / Copyright (c) 2017 The Tokio Authorsfutures-core0.3.34 — Copyright (c) 2016 Alex Crichton / Copyright (c) 2017 The Tokio Authorsfutures-executor0.3.34 — Copyright (c) 2016 Alex Crichton / Copyright (c) 2017 The Tokio Authorsfutures-io0.3.34 — Copyright (c) 2016 Alex Crichton / Copyright (c) 2017 The Tokio Authorsfutures-macro0.3.34 — Copyright (c) 2016 Alex Crichton / Copyright (c) 2017 The Tokio Authorsfutures-sink0.3.34 — Copyright (c) 2016 Alex Crichton / Copyright (c) 2017 The Tokio Authorsfutures-task0.3.34 — Copyright (c) 2016 Alex Crichton / Copyright (c) 2017 The Tokio Authorsfutures-util0.3.34 — Copyright (c) 2016 Alex Crichton / Copyright (c) 2017 The Tokio Authorsgetrandom0.2.17 — Copyright (c) 2018-2024 The rust-random Project Developers / Copyright (c) 2014 The Rust Project Developersgetrandom0.4.3 — Copyright (c) 2018-2026 The rust-random Project Developers / Copyright (c) 2014 The Rust Project Developersglam0.30.10 — Copyright 2020 Cameron Hartglob0.3.4 — Copyright (c) 2014 The Rust Project Developersgpu-allocator0.28.0 — Copyright 2021 Traverse Research B.V. / Copyright (c) 2021 Traverse Research B.V.hashbrown0.16.1 — Copyright (c) 2016 Amanieu d’Antrashashbrown0.17.1 — Copyright (c) 2016 Amanieu d’Antrasheck0.5.0 — Copyright (c) 2015 The Rust Project Developershttp1.5.0 — Copyright 2017 http-rs authors / Copyright (c) 2017 http-rs authorshttparse1.10.1 — Copyright (c) 2015-2025 Sean McArthurhttpdate1.0.3 — Copyright (c) 2016 Pyfischhybrid-array0.4.15 — Copyright (c) 2022-2026 The RustCrypto Project Developersidna1.1.0 — Copyright (c) 2013-2025 The rust-url developersipnet2.12.2 — Copyright 2017 Juniper Networks, Inc.jni-sys0.3.1 — Copyright (c) 2015 The rust-jni-sys Developersjni-sys0.4.1 — Copyright (c) 2015 The rust-jni-sys Developersjs-sys0.3.106 — Copyright (c) 2014 Alex Crichtonlitrs1.0.1 — Copyright (c) 2020 Project Developerslock_api0.4.14 — Copyright (c) 2016 The Rust Project Developerslog0.4.34 — Copyright (c) 2014 The Rust Project Developersmemmap20.5.10 — Copyright (c) 2020 Yevhenii Reizner / Copyright (c) 2015 Dan Burkertmemmap20.9.12 — Copyright (c) 2020 Yevhenii Reizner / Copyright (c) 2015 Dan Burkertnaga30.0.1 — Copyright (c) 2025 The gfx-rs developersnaga-types30.0.1 — Copyright (c) 2025 The gfx-rs developersnum-traits0.2.19 — Copyright (c) 2014 The Rust Project Developersnum_cpus1.17.0 — Copyright (c) 2015-2025 Sean McArthurosmpbf0.3.8 — Copyright (c) 2017 Johannes Hofmann mail@b-r-u.orgparking_lot0.12.5 — Copyright (c) 2016 The Rust Project Developersparking_lot_core0.9.12 — Copyright (c) 2016 The Rust Project Developerspercent-encoding2.3.2 — Copyright (c) 2013-2025 The rust-url developerspkg-config0.3.34 — Copyright (c) 2014 Alex Crichtonpng0.18.1 — Copyright (c) 2015 nwinpresser0.3.1 — Copyright (c) 2019 Embark Studiosproptest1.12.0 — Copyright (c) 2016 FullContact, Incquinn0.11.12 — Copyright (c) 2018 The quinn Developersquinn-proto0.11.19 — Copyright (c) 2018 The quinn Developersquinn-udp0.5.16 — Copyright (c) 2018 The quinn Developersrand0.10.3 — Copyright 2018 Developers of the Rand project / Copyright (c) 2014 The Rust Project Developersrand_core0.10.1 — Copyright (c) 2018-2026 The Rand Project Developersrand_pcg0.10.2 — Copyright (c) 2014-2017 Melissa O’Neill and PCG Project contributors / Copyright 2018 Developers of the Rand projectrand_xorshift0.5.0 — Copyright 2018 Developers of the Rand project / Copyright (c) 2014 The Rust Project Developersrange-alloc0.1.5 — Copyright (c) 2023 The gfx-rs developersrayon1.12.0 — Copyright (c) 2010 The Rust Project Developersrayon-core1.13.0 — Copyright (c) 2010 The Rust Project Developersregex1.13.1 — Copyright (c) 2014 The Rust Project Developersregex-automata0.4.18 — Copyright (c) 2014 The Rust Project Developersregex-syntax0.8.11 — Copyright (c) 2014 The Rust Project Developersrenderdoc-sys1.1.0 — Copyright (c) 2022 Eyal Kalderonreqwest0.12.28 — Copyright 2016 Sean McArthur / Copyright (c) 2016-2025 Sean McArthurrustls-pki-types1.15.1 — Copyright 2023 Dirkjan Ochtman / Copyright (c) 2023 Dirkjan Ochtman dirkjan@ochtman.nlscopeguard1.2.0 — Copyright (c) 2016-2019 Ulrik Sverdrup “bluss” and scopeguard developerssha20.11.1 — Copyright (c) 2016-2026 The RustCrypto Project Developers / Copyright (c) 2016 Artyom Pavlov / Copyright (c) 2009-2013 Mozilla Foundationshlex2.0.1 — Copyright 2015 Nicholas Allegra (comex). / Copyright (c) 2015 Nicholas Allegra (comex).siphasher1.0.4 — Copyright 2012-2016 The Rust Project Developers. / Copyright 2016-2026 Frank Denis.smallvec1.16.4 — Copyright (c) 2018 The Servo Project Developerssocket20.6.5 — Copyright (c) 2014 Alex Crichtonstable_deref_trait1.2.1 — Copyright (c) 2017 Robert Grossestatic_assertions1.1.0 — Copyright (c) 2017 Nikolai Vazqueztempfile3.27.0 — Copyright (c) 2015 Steven Allentokio-rustls0.26.6 — Copyright 2017 quininer kel / Copyright (c) 2017 quininer keltypenum1.20.1 — Copyright 2014 Paho Lurie-Gregg / Copyright (c) 2014 Paho Lurie-Greggunicode-width0.2.2 — Copyright (c) 2015 The Rust Project Developersurl2.5.8 — Copyright (c) 2013-2025 The rust-url developerswasm-bindgen0.2.129 — Copyright (c) 2014 Alex Crichtonwasm-bindgen-futures0.4.79 — Copyright (c) 2014 Alex Crichtonwasm-bindgen-macro0.2.129 — Copyright (c) 2014 Alex Crichtonwasm-bindgen-macro-support0.2.129 — Copyright (c) 2014 Alex Crichtonwasm-bindgen-shared0.2.129 — Copyright (c) 2014 Alex Crichtonweb-sys0.3.106 — Copyright (c) 2014 Alex Crichtonweb-time1.1.0 — Copyright 2023 dAxpeDDa / Copyright (c) 2023 dAxpeDDawgpu30.0.1 — Copyright (c) 2025 The gfx-rs developerswgpu-core30.0.1 — Copyright (c) 2025 The gfx-rs developerswgpu-core-deps-apple30.0.1 — Copyright (c) 2025 The gfx-rs developerswgpu-core-deps-emscripten30.0.1 — Copyright (c) 2025 The gfx-rs developerswgpu-core-deps-wasm30.0.1 — Copyright (c) 2025 The gfx-rs developerswgpu-core-deps-windows-linux-android30.0.1 — Copyright (c) 2025 The gfx-rs developerswgpu-hal30.0.1 — Copyright (c) 2025 The gfx-rs developerswgpu-naga-bridge30.0.1 — Copyright (c) 2025 The gfx-rs developerswgpu-types30.0.1 — Copyright (c) 2025 The gfx-rs developerslru-slab0.1.3 — Copyright (c) 2024 The lru-slab Developersraw-window-handle0.6.2 — Copyright (c) 2019 Osspial / Copyright (c) 2020 Osspialminiz_oxide0.8.9 — Copyright 2013-2014 RAD Game Tools and Valve Software / Copyright 2010-2014 Rich Geldreich and Tenacious Software LLC / Copyright (c) 2017 Frommiminiz_oxide0.9.1 — Copyright 2013-2014 RAD Game Tools and Valve Software / Copyright 2010-2014 Rich Geldreich and Tenacious Software LLC / Copyright (c) 2017 Frommicore_detect1.0.0 — Copyright (c) 2017-2020 The Rust Project Developerscriterion-plot0.5.0 — Copyright (c) 2014 Jorge Apariciofallible-iterator0.3.0 — Copyright (c) 2015 The rust-openssl-verify Developersfallible-streaming-iterator0.1.9 — Copyright (c) 2016 The fallible-streaming-iterator Developersitertools0.10.5 — Copyright (c) 2015minimal-lexical0.2.1 — Copyright (c) 2009 The Go Authors. All rights reserved.plain0.2.3 — Copyright (c) 2017 Plain contributorsquick-error1.2.3 — Copyright (c) 2015 The quick-error Developersr2d20.8.10 — Copyright (c) 2014-2016 Steven Facklerrusty-fork0.3.1 — Copyright (c) 2016 FullContact, Incscheduled-thread-pool0.2.7 — Copyright (c) 2014-2017 Steven Facklerserde_urlencoded0.7.1 — Copyright (c) 2016 Anthony Raminevcpkg0.2.15 — Copyright (c) 2017 Jim McGrathversion_check0.9.5 — Copyright (c) 2017-2018 Sergio Benitezwait-timeout0.2.1 — Copyright (c) 2014 Alex Crichtonicu_collections2.4.0 — Copyright © 2020-2024 Unicode, Inc. / ICU 1.8.1 to ICU 57.1 © 1995-2016 International Business Machines Corporation and others.icu_locale_core2.4.0 — Copyright © 2020-2024 Unicode, Inc. / ICU 1.8.1 to ICU 57.1 © 1995-2016 International Business Machines Corporation and others.icu_normalizer2.4.0 — Copyright © 2020-2024 Unicode, Inc. / ICU 1.8.1 to ICU 57.1 © 1995-2016 International Business Machines Corporation and others.icu_normalizer_data2.4.0 — Copyright © 2020-2024 Unicode, Inc. / ICU 1.8.1 to ICU 57.1 © 1995-2016 International Business Machines Corporation and others.icu_properties2.4.0 — Copyright © 2020-2024 Unicode, Inc. / ICU 1.8.1 to ICU 57.1 © 1995-2016 International Business Machines Corporation and others.icu_properties_data2.4.0 — Copyright © 2020-2024 Unicode, Inc. / ICU 1.8.1 to ICU 57.1 © 1995-2016 International Business Machines Corporation and others.icu_provider2.4.0 — Copyright © 2020-2024 Unicode, Inc. / ICU 1.8.1 to ICU 57.1 © 1995-2016 International Business Machines Corporation and others.litemap0.8.3 — Copyright © 2020-2024 Unicode, Inc. / ICU 1.8.1 to ICU 57.1 © 1995-2016 International Business Machines Corporation and others.potential_utf0.1.6 — Copyright © 2020-2024 Unicode, Inc. / ICU 1.8.1 to ICU 57.1 © 1995-2016 International Business Machines Corporation and others.tinystr0.8.5 — Copyright © 2020-2024 Unicode, Inc. / ICU 1.8.1 to ICU 57.1 © 1995-2016 International Business Machines Corporation and others.writeable0.6.4 — Copyright © 2020-2024 Unicode, Inc. / ICU 1.8.1 to ICU 57.1 © 1995-2016 International Business Machines Corporation and others.yoke0.8.4 — Copyright © 2020-2024 Unicode, Inc. / ICU 1.8.1 to ICU 57.1 © 1995-2016 International Business Machines Corporation and others.yoke-derive0.8.4 — Copyright © 2020-2024 Unicode, Inc. / ICU 1.8.1 to ICU 57.1 © 1995-2016 International Business Machines Corporation and others.zerofrom0.1.8 — Copyright © 2020-2024 Unicode, Inc. / ICU 1.8.1 to ICU 57.1 © 1995-2016 International Business Machines Corporation and others.zerofrom-derive0.1.8 — Copyright © 2020-2024 Unicode, Inc. / ICU 1.8.1 to ICU 57.1 © 1995-2016 International Business Machines Corporation and others.zerotrie0.2.5 — Copyright © 2020-2024 Unicode, Inc. / ICU 1.8.1 to ICU 57.1 © 1995-2016 International Business Machines Corporation and others.zerovec0.11.9 — Copyright © 2020-2024 Unicode, Inc. / ICU 1.8.1 to ICU 57.1 © 1995-2016 International Business Machines Corporation and others.zerovec-derive0.11.7 — Copyright © 2020-2024 Unicode, Inc. / ICU 1.8.1 to ICU 57.1 © 1995-2016 International Business Machines Corporation and others.aho-corasick1.1.5 — Copyright (c) 2015 Andrew Gallantbyteorder1.5.0 — Copyright (c) 2015 Andrew Gallantbyteorder-lite0.1.0 — Copyright (c) 2015 Andrew Gallantmemchr2.8.3 — Copyright (c) 2015 Andrew Gallanttermcolor1.4.1 — Copyright (c) 2015 Andrew Gallantwinapi-util0.1.11 — Copyright (c) 2017 Andrew Gallantsame-file1.0.6 — Copyright (c) 2017 Andrew Gallantwalkdir2.5.0 — Copyright (c) 2015 Andrew Gallantfoldhash0.2.0 — Copyright (c) 2024 Orson Petersslotmap1.1.1 — Copyright (c) 2021 Orson Peters orsonpeters@gmail.combytemuck1.25.2 — Copyright (c) 2019 Daniel “Lokathor” Gee.bytemuck_derive1.12.1 — Copyright (c) 2019 Daniel “Lokathor” Gee.tinyvec1.13.3 — Copyright (c) 2019 Daniel “Lokathor” Gee.
Prior art & inspiration
Credits for projects this SDK learns from. Where only a design was learned from, no code was copied; where an entry says logic was ported, the derived work is credited and its license text is retained under docs/THIRD_PARTY_LICENSES/.
- MapLibre — its open map renderers and how they frame tile scheduling, frustum listing and tile budgeting are the mature prior art this SDK’s renderer seams knowingly re-implement from first principles.
- OpenStreetMap and its ODbL database — the map-data backbone this SDK renders; the ODbL attribution obligations above are real and are honored in every reference app.
- UniFFI — whose UDL-first, contract-as-single-source-of-truth binding approach this SDK adopts for its Kotlin/Swift/React Native FFI surface.
- wgpu — the portable WebGPU-in-Rust stack that lets one GPU rendering path run natively, on mobile, and in the browser.
- Navara — MapLibre-affiliated web 3D-globe / GIS visualization engine (Rust/WASM headless GIS core); credited as surveyed prior art for headless-engine architecture, not reused and not a reference for this SDK’s navigation capabilities.
- Ferrostar — Stadia Maps’ Rust navigation core. Its trip-state / step-advance-condition design is the reference this SDK’s
map-navigationcrate follows closely: the step-advance conditions and their recommended defaults are modeled on Ferrostar’s (including its rapid-double-advance hazard, which this SDK’s ports avoid), and its deviation-detection and spoken-instruction models informed this SDK’s seams. Because this SDK’s navigation logic is derived from Ferrostar’s design, its license text is retained indocs/THIRD_PARTY_LICENSES/ferrostar-LICENSE.txt(BSD 3-Clause, Copyright (c) 2023, Stadia Maps, Inc.); no file in this workspace is a verbatim copy, and this SDK’s own code remains MIT OR Apache-2.0.
API reference (rustdoc)
The rustdoc for the workspace crates ships at the api/ path of this
site: start at geospatial-types,
the record and math layer every other crate builds on.
This tree is generated, not hand-maintained. The build script runs
cargo doc against the source commit named in provenance.txt on every
publish, and stages the rendered tree at api/ without post-processing.
Pages under api/ therefore describe exactly that commit; a newer
publish replaces the whole tree at the same paths.
The FFI contract
The SDK’s binding surface is one frozen UniFFI UDL contract,
map_native_core.udl, published verbatim below and as a plain-text
download. This file is the single source of
truth: the Kotlin, Swift, and React Native bindings are generated from
it, and the WebAssembly TypeScript mirror is regression-checked against
it.
The contract is addition-only. Existing members keep their signature and meaning, so your compiled bindings stay valid across upgrades; new capability is new members. The API reference documents the same surface as generated rustdoc; the UDL download carries the raw form machine tooling consumes.
The pages below show the contract verbatim, byte-equality checked against the source file on every publish; an edit to the contract propagates to the site through the build or the build fails.
Show the raw UDL contract
// FROZEN (M4, slice 1 — 2026-10-27) — the UniFFI contract of the Maps SDK;
// extended in M4 slice 2 with the fit-bounds constructor (`EdgeInsetsPixels`
// and the named `MapCamera` constructor), in M4 slice 3 with the async
// provider surface (the `MapSdk` object resolving the `geospatial-traits`
// provider contracts one-to-one through the `map-core` `ProviderRegistry`,
// the provider records and enums mirror of `geospatial-types`, and the
// provider failure variants of `SdkError`), in M4 slice 4 with the
// foreign-callback provider injection: the `GeocodingProvider` and
// `RasterTileProvider` `[Trait, WithForeign]` interfaces foreign code
// (Kotlin, Swift) implements and registers on `MapSdk` through the
// `register_*_from_foreign` methods, and in M6 with the navigation wiring:
// the `NavigationStateEventListener` `[Trait, WithForeign]` listener and the
// `MapNavigationController` object driving the `map-navigation` session, with
// the mirror records of the navigation state, the instruction payload and the
// policy records standing in for the native decision objects.
//
// M2's remaining developer-facing sub-item (2026-10-28 tile TTL/budget
// ruling) adds the tile-cache settings surface additively: the
// `TileCacheTerms` record and the `MapSdk::set_raster_tile_cache_time_to_live`
// / `raster_tile_cache_terms` pair over the installed tile cache (byte
// budget stays registration-time through the frozen registration method).
//
// Round 12 (owner ruling of 2026-10-29) adds the provider-failure diagnostic
// seam additively: the `ProviderFailureClassification` enum and the
// `ProviderFailureDiagnostic` record, plus the
// `MapSdk::latest_provider_failure_diagnostics` readback over the bounded
// per-object ring the foreign-callback provider adapters write when a
// foreign-implemented provider contract call fails.
//
// This file is the single source of truth for the native binding surface
// (Kotlin, Swift, and React Native via `uniffi-bindgen-react-native`; PRD §6,
// decision 1). The hand-reviewed wasm surface in `crate map-web-bindings`
// mirrors this contract, and a M4 slice keeps a contract-sync test asserting
// that parity (PRD §4.7).
//
// This slice freezes the camera/projection core of `map-rendering`: the
// anchored `MapCamera`, the free projection helpers, and the version probe,
// with names and semantics aligned one-to-one with the wasm surface.
//
// Records mirror the projection core's value shapes by field name; they are
// plain FFI records, converted on the Rust side to and from the
// `geospatial-types` `GeographicCoordinate`/`BoundingBox` and the
// `map-rendering` viewport/tile types through explicit converters, because
// FFI traits cannot be implemented for foreign-crate types. The error
// carries detail messages and mirrors the `geospatial-types` `SDKError`
// variants these calls can actually produce; camera/projection failures are
// `InvalidCoordinateInput` — the viewport-extent and box-corner failures of
// the core map to the same variant and keep their verbatim message under
// `detail` — while the provider surface maps the six `SDKError` variants the
// registry path can produce. Further variants enter the contract as later
// slices surface real error paths; none are invented here.
namespace map_native_core {
/// Version of the compiled SDK, equal to the crate's package version.
string get_sdk_compiled_version();
/// Maps a coordinate to normalized world coordinates in `[0, 1]`² (x
/// eastward from the antimeridian, y southward from the north pole);
/// latitude is clamped to the Mercator cutoffs.
[Throws=SdkError]
NormalizedWorldCoordinate coordinate_to_normalized_world(GeographicCoordinate coordinate);
/// The XYZ address of the tile containing the coordinate at the zoom
/// level.
[Throws=SdkError]
TileKeyRecord tile_key_for_coordinate(GeographicCoordinate coordinate, u8 zoom_level);
/// Every XYZ tile whose area intersects the spanning box, ordered
/// row-major from the north.
[Throws=SdkError]
sequence<TileKeyRecord> tiles_covering(BoundingBox bounding_box, u8 zoom_level);
/// The SDK default step-advance policy: the distance-to-end-of-step
/// condition with the Ferrostar recommended driving defaults.
StepAdvancePolicy default_step_advance_policy();
/// The SDK default deviation policy: the distance-threshold detector with
/// the SDK's default deviation threshold.
DeviationPolicy default_deviation_policy();
/// The SDK default reroute-recalculation policy (round 11): the
/// Ferrostar-recommended hysteresis — a 5 s cooldown and a 50 m movement
/// threshold.
ReroutePolicy default_reroute_policy();
};
/// A position on the WGS 84 ellipsoid in decimal degrees.
dictionary GeographicCoordinate {
/// Latitude in decimal degrees, valid within `[-90, 90]`.
double latitude;
/// Longitude in decimal degrees, valid within `[-180, 180]`.
double longitude;
};
/// Rectangular area between a south-west and a north-east corner; boxes
/// never span the antimeridian.
dictionary BoundingBox {
/// South-west corner (minimum latitude and longitude).
GeographicCoordinate south_west;
/// North-east corner (maximum latitude and longitude).
GeographicCoordinate north_east;
};
/// Position on the viewport in physical pixels (origin top-left, y down).
dictionary ScreenPoint {
/// Horizontal position in pixels.
double x;
/// Vertical position in pixels.
double y;
};
/// A world position in normalized world coordinates, both axes within
/// `[0, 1]`.
dictionary NormalizedWorldCoordinate {
/// Eastward position from the antimeridian.
double x;
/// Southward position from the north pole.
double y;
};
/// One web-mercator XYZ tile address.
dictionary TileKeyRecord {
/// Tile column, in `0..2^zoom_level`.
u32 tile_x;
/// Tile row, counted from the north, in `0..2^zoom_level`.
u32 tile_y;
/// Zoom level this tile belongs to.
u8 zoom_level;
};
/// Viewport dimensions in physical pixels.
dictionary ViewportPixels {
/// Horizontal extent in pixels.
double width_pixels;
/// Vertical extent in pixels.
double height_pixels;
};
/// Screen-space insets kept clear of map content: distances from each
/// viewport edge in pixels, so UI overlays — route cards, attribution, sheet
/// panels — never cover the content the camera guarantees visible.
dictionary EdgeInsetsPixels {
/// Pixels kept clear from the left viewport edge.
double left;
/// Pixels kept clear from the top viewport edge.
double top;
/// Pixels kept clear from the right viewport edge.
double right;
/// Pixels kept clear from the bottom viewport edge.
double bottom;
};
/// Failures thrown across the native binding surface of this slice. The
/// variants mirror the `geospatial-types` `SDKError` subset actually returned
/// by the frozen calls; the detail message is always verbatim.
[Error] interface SdkError {
/// A coordinate, viewport extent or box corner failed validation.
InvalidCoordinateInput(string detail);
/// The network transport failed; carries the provider's verbatim detail.
NetworkRequestFailed(string detail);
/// The provider answered with a payload the parser could not use.
ResponseParsingFailure(string detail);
/// The provider returned a documented application-level failure status.
ProviderRequestRejected(string detail);
/// The token-bucket rate limiter is exhausted; retry after the next refill.
RateLimitExhausted();
/// No provider implementation is registered for the requested contract.
ProviderNotConfigured(string contract_name);
/// A navigation input (route record or navigation policy) failed native
/// validation.
InvalidNavigationInput(string detail);
/// An overlay descriptor did not describe a usable overlay (wrong kind
/// payload, an out-of-range color channel, or a non-positive or
/// non-finite width or radius), or a surface input was structurally
/// unusable (a null, zero or ambiguous platform surface pointer, or a
/// non-positive viewport extent). Round 8 (surface slice).
InvalidRenderInput(string detail);
/// The live rendering could not proceed on the platform surface: the
/// platform handle or surface was unusable, the graphics context could
/// not be acquired, the surface format could not be negotiated or
/// configured, the frame texture could not be acquired even after the
/// stale-surface retry, or the frame's render state was busy in another
/// draw. Round 8 (surface slice).
RenderingSurfaceUnavailable(string detail);
};
/// Anchored map camera (geographic center, zoom, bearing, pitch) mirroring
/// the native `map-rendering` camera. The ground point under the center
/// always sits at the viewport center; zoom and pitch are clamped and the
/// bearing wraps into `[0, 360)` exactly like the native constructor.
interface MapCamera {
[Throws=SdkError]
constructor(GeographicCoordinate center, double zoom_level, double bearing_degrees, double pitch_degrees);
/// Builds the camera that frames a bounding box inside the viewport's
/// inset region: top-down and north-up by construction (pitch and bearing
/// zero), zoomed to the largest level keeping the whole box inside the
/// inset area and clamped to `maximum_zoom_level`; the center is anchored
/// on the inset region's center. Throws when the insets or the maximum
/// zoom are not finite usable values, when the insets are negative or
/// leave no inset area, or when the box spans no positive world area.
[Throws=SdkError, Name=fit_bounds]
constructor(BoundingBox bounding_box, ViewportPixels viewport, EdgeInsetsPixels edge_insets, double maximum_zoom_level);
/// Projects a coordinate to viewport pixels (origin top-left, y down).
/// Returns null when the point lies behind the camera.
[Throws=SdkError]
ScreenPoint? project_to_screen(GeographicCoordinate coordinate, ViewportPixels viewport, double field_of_view_degrees);
/// Unprojects a viewport pixel back onto the ground plane. Returns null
/// when the ray misses the ground (typically below the horizon at steep
/// pitch).
[Throws=SdkError]
GeographicCoordinate? screen_to_ground_coordinate(ScreenPoint screen_point, ViewportPixels viewport, double field_of_view_degrees);
/// Every tile at `tile_zoom_level` whose area intersects the visible
/// ground, sorted row-major from the north.
[Throws=SdkError]
sequence<TileKeyRecord> visible_tile_keys(ViewportPixels viewport, double field_of_view_degrees, u8 tile_zoom_level);
/// Nearest whole zoom level a tile fetch should use for this camera.
u8 tile_zoom_level();
/// Reads the camera's attribute state back verbatim as it was built
/// (round-9 asks). The getters make `fit_bounds`' chosen framing
/// readable: the zoom level the constructor actually fitted, its
/// anchored center, and the constructed bearing and pitch read back
/// through these members. Read-only: the camera carries no setters —
/// a new camera pose is a new constructor call. The UniFFI 0.32 UDL
/// parser supports no `[Attribute]` member syntax, so the getters are
/// plain methods.
GeographicCoordinate center();
double zoom_level();
double bearing_degrees();
double pitch_degrees();
/// Ground meters represented by one screen pixel at the camera's center
/// (round 10, the scale bar's helper): the equatorial ground meters per
/// pixel at the camera's zoom level times the cosine of the center
/// latitude, so the answer shrinks toward the poles exactly like the
/// projected ground. At any nonzero pitch this is an approximation valid
/// at the viewport's center column: the helper is flat-centered (no
/// unprojection fold of pitch into the answer) and documents that
/// honestly; the platform scale bars fit the meter magnitude through a
/// labeled ladder rather than reading a bare value.
[Throws=SdkError]
double ground_meters_per_screen_pixel(ViewportPixels viewport, double field_of_view_degrees);
};
// ---------------------------------------------------------------------------
// Foreign-callback provider surface (M4 slice 4). The `[Trait, WithForeign]`
// interfaces below are the provider contracts foreign code (Kotlin, Swift)
// implements to serve calls of the matching `MapSdk` method. The plain
// contract name is used — no `Foreign` prefix — because this namespace
// freezes nothing else with those names (the `geospatial-traits` traits of
// the same names live in a different crate and never cross this FFI), and
// the name a Kotlin implementor reads is the contract the SDK documents.
// Per-member `[Async]` only: interface-level `[Async]` is rejected by the
// uniffi 0.32 UDL parser (documented in `docs/udl/contracts-draft.udl`).
// The remaining provider contracts follow this same two-step pattern as
// their first native or foreign implementations demand.
// ---------------------------------------------------------------------------
/// Geocoding contract implemented by foreign code and registered on `MapSdk`
/// through `register_geocoding_provider_from_foreign`.
[Trait, WithForeign]
interface GeocodingProvider {
/// Resolves a free-form address text into candidate places.
[Async, Throws=SdkError]
sequence<PlaceResult> forward_geocode(string address_text);
/// Resolves a coordinate into candidate places describing that position.
[Async, Throws=SdkError]
sequence<PlaceResult> reverse_geocode(GeographicCoordinate coordinate);
};
/// Raster-tile contract implemented by foreign code and registered on
/// `MapSdk` through `register_raster_tile_provider_from_foreign`.
[Trait, WithForeign]
interface RasterTileProvider {
/// Fetches one web-mercator XYZ raster tile at the requested resolution.
[Async, Throws=SdkError]
RasterTileData fetch_raster_tile(
u32 tile_x,
u32 tile_y,
u8 zoom_level,
RasterImageFormat image_format,
RasterTileResolution tile_resolution
);
};
// ---------------------------------------------------------------------------
// Async provider surface (M4 slice 3). The enums and records below mirror the
// `geospatial-types` provider records one-to-one by name and field; `MapSdk`
// resolves every call through the `map-core` `ProviderRegistry`, so a missing
// registration throws `ProviderNotConfigured` with the contract name.
// ---------------------------------------------------------------------------
/// Structural precision a geocoder reached for one resolved location.
enum GeocodeQuality {
/// Rooftop or feature point.
"Point",
/// Street address matched to the building level.
"StreetAddress",
/// Street intersection.
"Intersection",
/// Street level without house number.
"Street",
/// Postal code area.
"PostalCode",
/// Settlement level (village, town, city).
"City",
/// County or administrative district.
"County",
/// First-level administrative division.
"State",
/// Sovereign country level.
"Country",
};
/// Mode of transport used to calculate a route.
enum TransportationMode {
/// Private car routing.
"Automobile",
/// Bicycle routing.
"Bicycle",
/// Walking routing.
"Pedestrian",
};
/// Length and speed conventions used when displaying a route.
enum UnitSystem {
/// Kilometers and kilometers per hour.
"Metric",
/// Miles and miles per hour.
"Imperial",
};
/// What a routing request optimizes for; the strategy applies to automobile
/// routing only (the current providers have no time-based cost model for the
/// other modes).
enum RouteStrategy {
/// Minimize expected travel time.
"Fastest",
/// Minimize traveled distance.
"Shortest",
};
/// Class of disruption reported in a traffic incident.
enum TrafficIncidentType {
/// Road construction.
"Construction",
/// Traffic-impacting event.
"Event",
/// Collision.
"Accident",
/// Weather-related disruption.
"Weather",
/// Lane or road closure.
"Closure",
};
/// Severity ranking of a traffic incident.
enum TrafficIncidentSeverity {
/// Minimal delay expected.
"Low",
/// Noticeable delay expected.
"Medium",
/// Significant delay expected.
"High",
};
/// Congestion classification of a road segment.
enum TrafficFlowCongestionLevel {
/// At or above free-flow speed.
"FreeFlow",
/// Moderately below free-flow speed.
"Slow",
/// Stop-and-go traffic.
"Congested",
/// Roadway effectively closed.
"Blocked",
};
/// Encoded image formats a raster tile provider may deliver.
enum RasterImageFormat {
/// PNG imagery.
"Png",
/// JPEG imagery.
"Jpeg",
};
/// Pixel resolution a raster tile is rendered at; `Standard` is the plain
/// tile form, `Retina` the `@2x` form with twice the texels per edge for the
/// same logical tile span.
enum RasterTileResolution {
/// One texel per logical tile pixel.
"Standard",
/// Twice the texels per logical tile pixel (the `@2x` request form).
"Retina",
};
/// One resolved location returned by forward or reverse geocoding.
dictionary PlaceResult {
/// Display string the provider produced for this place.
string structured_address_text;
/// Resolved position.
GeographicCoordinate coordinate;
/// Structural precision reached for this resolution.
GeocodeQuality geocode_quality;
};
/// Road features a routing request can ask the provider to avoid; all
/// disabled by default.
dictionary AvoidOptions {
/// Avoid toll roads.
boolean toll_roads;
/// Avoid limited-access highways and motorways.
boolean limited_access_highways;
/// Avoid ferry crossings.
boolean ferries;
/// Avoid unpaved roads.
boolean unpaved_roads;
};
/// Options controlling how a route is calculated.
dictionary RouteOptions {
/// Mode of transport.
TransportationMode transportation_mode;
/// Whether routing optimizes travel time or distance.
RouteStrategy route_strategy;
/// Display unit convention the provider should annotate.
UnitSystem unit_system;
/// Features to avoid.
AvoidOptions avoid_options;
/// Departure instant as seconds since the Unix epoch; null for
/// "depart now".
u64? departure_timestamp_seconds;
};
/// One maneuver within a route leg.
dictionary RouteStep {
/// Distance of this step in meters.
double distance_meters;
/// Travel time of this step in seconds.
double duration_seconds;
/// Maneuver classification as a stable snake-case text label; the
/// roundabout exit index (one-based) and the turn direction are carried
/// in the label (`roundabout-2`, `turn-left`); lane guidance is not part
/// of this surface yet.
string maneuver_type;
/// Human-readable instruction in the display language.
string instruction_display_text;
/// Instruction tuned for text-to-speech engines.
string instruction_speech_text;
/// Step geometry from the previous step end to this maneuver point.
sequence<GeographicCoordinate> shape;
};
/// Leg between two consecutive waypoints.
dictionary RouteLeg {
/// Leg distance in meters.
double distance_meters;
/// Leg travel time in seconds.
double duration_seconds;
/// Maneuver steps in traversal order.
sequence<RouteStep> steps;
};
/// A complete calculated route.
dictionary RouteData {
/// Total distance across all legs, in meters.
double distance_meters;
/// Total travel time across all legs, in seconds.
double duration_seconds;
/// Geographic bounds of the full route geometry.
BoundingBox bounding_box;
/// Legs, one per consecutive waypoint pair.
sequence<RouteLeg> legs;
};
/// One origin-destination cell of a distance matrix.
dictionary DistanceMatrixEntry {
/// Origin index into the request's origins.
u32 origin_index;
/// Destination index into the request's destinations.
u32 destination_index;
/// Distance for this cell in meters.
double distance_meters;
/// Travel time for this cell in seconds.
double duration_seconds;
};
/// Travel-time matrix for origins x destinations.
dictionary DistanceMatrix {
/// Entries in row-major origin-major order.
sequence<DistanceMatrixEntry> entries;
};
/// One reported disruption inside a queried bounding box.
dictionary TrafficIncident {
/// Provider-stable incident identifier.
string identifier;
/// Class of disruption.
TrafficIncidentType incident_type;
/// Severity ranking.
TrafficIncidentSeverity severity;
/// Provider display text for this incident.
string short_description_text;
/// Position where the incident starts.
GeographicCoordinate start_coordinate;
/// Position where the incident ends, if reported.
GeographicCoordinate? end_coordinate;
};
/// Flow condition for one road segment (or polyline of road segments) inside
/// the queried box, in kilometers per hour when the provider reports speeds
/// and as a congestion classification otherwise.
dictionary TrafficFlowData {
/// Segment geometry, ordered along the road.
sequence<GeographicCoordinate> shape_coordinates;
/// Currently observed speed in kilometers per hour, when reported.
double? current_speed_kilometers_per_hour;
/// Unconstrained (free-flow) speed in kilometers per hour, when reported.
double? free_flow_speed_kilometers_per_hour;
/// Congestion classification from the speed ratio or provider buckets.
TrafficFlowCongestionLevel congestion_level;
};
/// One fetched web-mercator XYZ raster tile.
dictionary RasterTileData {
/// Tile column coordinate at the given zoom level.
u32 tile_x;
/// Tile row coordinate at the given zoom level.
u32 tile_y;
/// Zoom level this tile belongs to.
u8 zoom_level;
/// Encoding of the byte payload.
RasterImageFormat image_format;
/// Pixel resolution the payload was rendered at.
RasterTileResolution tile_resolution;
/// Encoded image bytes.
sequence<u8> image_bytes;
};
/// Async provider entry point of the SDK: resolves every call of the
/// implemented `geospatial-traits` contracts through the `map-core`
/// `ProviderRegistry`. Construct the object empty, then the native
/// integrator registers providers through the Rust-side `register_*`
/// methods and foreign integrators through the `register_*_from_foreign`
/// methods (see the `[Trait, WithForeign]` interfaces above); every call
/// into an unregistered contract throws `SdkError::ProviderNotConfigured`
/// with the contract name.
///
/// The methods mirror the provider contracts one-to-one: geocoding (forward
/// and reverse), routing, distance matrix, place search, traffic incidents,
/// traffic flow and raster tiles. Elevation and offline routing follow with
/// their first concrete provider implementations (M6/M7).
interface MapSdk {
/// Builds the SDK with an unconfigured provider registry; the native
/// integrator registers providers before the first await call.
constructor();
/// Registers the geocoding implementation built by the foreign code on
/// the `GeocodingProvider` interface; the foreign implementation starts
/// serving `forward_geocode` and `reverse_geocode` once this returns.
void register_geocoding_provider_from_foreign(GeocodingProvider geocoding_provider);
/// Registers the raster-tile implementation built by the foreign code on
/// the `RasterTileProvider` interface; the foreign implementation starts
/// serving `fetch_raster_tile` once this returns.
void register_raster_tile_provider_from_foreign(RasterTileProvider raster_tile_provider);
/// Resolves a free-form address text into candidate places.
[Async, Throws=SdkError]
sequence<PlaceResult> forward_geocode(string address_text);
/// Resolves a coordinate into candidate places describing that position.
[Async, Throws=SdkError]
sequence<PlaceResult> reverse_geocode(GeographicCoordinate coordinate);
/// Calculates a route through the ordered waypoints with the given
/// options; the waypoints must all be canonical coordinates.
[Async, Throws=SdkError]
RouteData calculate_route(sequence<GeographicCoordinate> waypoints, RouteOptions route_options);
/// Calculates durations and distances for every origin-destination pair.
[Async, Throws=SdkError]
DistanceMatrix calculate_distance_matrix(
sequence<GeographicCoordinate> origins,
sequence<GeographicCoordinate> destinations,
TransportationMode transportation_mode
);
/// Returns fast completion candidates while the user types; the boundary
/// restricts the search when given.
[Async, Throws=SdkError]
sequence<PlaceResult> search_autocomplete(string query_text, BoundingBox? search_boundary);
/// Returns full search results for a settled query; the boundary
/// restricts the search when given.
[Async, Throws=SdkError]
sequence<PlaceResult> search_places(string query_text, BoundingBox? search_boundary);
/// Fetches incidents intersecting the bounding box.
[Async, Throws=SdkError]
sequence<TrafficIncident> fetch_traffic_incidents(BoundingBox bounding_box);
/// Fetches per-segment flow observations inside the bounding box.
[Async, Throws=SdkError]
sequence<TrafficFlowData> fetch_traffic_flow(BoundingBox bounding_box);
/// Fetches one web-mercator XYZ raster tile at the requested resolution.
[Async, Throws=SdkError]
RasterTileData fetch_raster_tile(
u32 tile_x,
u32 tile_y,
u8 zoom_level,
RasterImageFormat image_format,
RasterTileResolution tile_resolution
);
/// Rounds 8 (surface slice): registers the seven native MapQuest
/// provider implementations (geocoding, routing, distance matrix, place
/// search, traffic incidents, traffic flow, raster tiles) onto the
/// registry, constructed from the MapQuest consumer key with the SDK
/// defaults for base URL, rate limiting and retries — the foreign
/// mirror of the native integrator's `register_*` calls, so foreign
/// code installs the whole provider set with one call and an API key.
/// Re-registration replaces every slot. Elevation stays unregistered:
/// the elevation contract has no MapQuest provider, and elevation calls
/// keep throwing `ProviderNotConfigured`.
[Throws=SdkError]
void register_mapquest_providers(string mapquest_api_key);
/// Round 9 (offline-maps demo): registers the seven native MapQuest
/// provider implementations from one key exactly as
/// `register_mapquest_providers`, wrapping the raster-tile contract in
/// the disk response cache opened (or created) as a SQLite database at
/// `tile_cache_database_path` under an absolute byte budget of
/// `tile_cache_absolute_maximum_bytes`. Cached tile responses follow the
/// tiles category's time-to-live (thirty days by default — the
/// imagery-provider terms ceiling, PRD §6.7); a cache miss goes to the
/// network and back-fills the cache, so pre-warmed tiles keep rendering
/// whenever the network is unavailable. The path must be non-empty and
/// the byte budget positive; a cache open or quota failure throws with
/// its verbatim detail. Re-registration replaces the slots (including
/// the cached raster-tile slot).
[Throws=SdkError]
void register_mapquest_providers_with_tile_cache(string mapquest_api_key, string tile_cache_database_path, u64 tile_cache_absolute_maximum_bytes);
// --- Tile-cache settings surface (M2's remaining developer-facing
// sub-item; addition-only). The frozen
// `register_mapquest_providers_with_tile_cache` signature is untouched:
// the byte budget stays a registration-time fact (the SQLite store's
// quota is fixed when the store opens, so retuning it means
// re-registering through the frozen method), while the tile time-to-live
// is a property of the installed cache and is tuned live through the two
// members below. A per-integration registration-time alternative (a
// second registration method carrying the whole frozen parameter set
// plus one optional TTL field) was rejected: it duplicates the
// registration surface for one optional value, and the terms readback
// below already makes the applied value inspectable, so the override
// rides the installed cache instead. Re-registration replaces the slots
// and the tuning: a freshly registered cache starts again at the
// category default, and the integrator re-applies the override after
// registering.
//
// Validation ruling (PRD §6.7's provider-terms-ceiling intent): a
// requested time-to-live above the installed provider's declared ceiling
// is ACCEPTED, and the ceiling clamps what the cache stores — the terms
// ceiling is a compliance bound on what the cache may retain, not a
// preference, so the SDK must never retain beyond the provider's terms
// whatever the integrator asks for; refusing the call would instead
// break a live integration every time the provider's terms move or the
// integrator reads a stale ceiling. The ceiling and the effective
// (clamped) time-to-live are both in the `raster_tile_cache_terms`
// readback, so an integrator sees exactly what went into force. A zero
// time-to-live is refused: it would expire every entry on capture
// (a cache that stores nothing), which is almost certainly a unit
// mistake in the caller.
/// Tile-cache settings surface (M2): overrides the raster-tile cache's
/// time-to-live on the installed cache, in seconds. The tile cache must
/// be installed (registered through
/// `register_mapquest_providers_with_tile_cache`); otherwise this throws
/// `ProviderNotConfigured` naming `TileCache` as the unconfigured
/// backing store. A zero value throws `InvalidCoordinateInput` with a
/// precise detail (a zero time-to-live expires every entry on capture,
/// storing nothing). A value above the installed provider's declared
/// terms ceiling is accepted with the ceiling clamping what the cache
/// stores (see the ruling above); `raster_tile_cache_terms` reports the
/// effective value.
[Throws=SdkError]
void set_raster_tile_cache_time_to_live(u64 time_to_live_seconds);
/// Tile-cache settings surface (M2): reads the effective raster-tile
/// caching terms so a developer can display them (a settings page, or
/// the offline-maps demo's cache panel). Reports the time-to-live
/// currently in force on the installed cache (the category default
/// thirty days, or the last override this object accepted, clamped when
/// it exceeded the provider's terms ceiling), the installed provider's
/// declared terms ceiling (null when the provider declares no ceiling,
/// in which case the requested time-to-live applies verbatim), and the
/// cache category's default time-to-live. The tile cache must be
/// installed (registered through
/// `register_mapquest_providers_with_tile_cache`); otherwise this
/// throws `ProviderNotConfigured` naming `TileCache` as the
/// unconfigured backing store. Read-only: the readback never mutates
/// the cache.
[Throws=SdkError]
TileCacheTerms raster_tile_cache_terms();
/// Round-10 diagnostics (capability 6): subscribes the foreign listener
/// to the SDK's structured internal log events streamed fire-and-forget
/// from the crates' bounded ring buffers. Only events at
/// `minimum_severity` or above are delivered, and coordinates within
/// event messages are always rounded at the sink (never the API key).
/// The listener callbacks run on the threads that produce the events and
/// must never block on the caller; re-subscribing an already subscribed
/// listener replaces its severity gate.
void subscribe_to_diagnostic_events(SdkDiagnosticEventListener listener, SdkLogSeverity minimum_severity);
/// Round-10 diagnostics: drains up to `maximum_count` of the most recent
/// ring events at `minimum_severity` or above through the listener's
/// `on_diagnostic_event` (oldest first) and returns how many were
/// delivered, then (like Organic Maps' and OsmAnd's opt-in capture)
/// nothing is removed from the ring, so a later flush can redeliver. The
/// flush is bounded and synchronous: a listener must not block on it.
u32 flush_recent_diagnostic_events(SdkDiagnosticEventListener listener, SdkLogSeverity minimum_severity, u32 maximum_count);
/// Round-12 provider-failure diagnostic seam (addition-only): reads the
/// retained diagnostics of every failed foreign-callback provider call
/// this object has served — the failures foreign implementations of the
/// `[Trait, WithForeign]` provider contracts threw, both the typed ones
/// (the foreign code threw one of the contract's `SdkError` variants) and
/// the unclassified ones (the foreign code failed without a mapped
/// error). Returns the ring's retained entries oldest first, in
/// recency-bounded order: the ring keeps the last
/// `PROVIDER_FAILURE_DIAGNOSTIC_RING_CAPACITY` failures and drops the
/// oldest beyond it, and reading is non-destructive. Read-only: the
/// readback never records anything and never clears the ring. See
/// `ProviderFailureDiagnostic` for what an entry carries; the frozen
/// `SdkError` variants are untouched — failures still surface typed at
/// the throwing call, and this seam only adds where they can be
/// classified after the fact.
sequence<ProviderFailureDiagnostic> latest_provider_failure_diagnostics();
};
// ---------------------------------------------------------------------------
// Tile-cache settings surface (M2 — addition-only). The record mirrors the
// effective caching terms of the installed raster-tile cache; the ceiling is
// the provider-specific caching term (PRD §6.7), read only when a cache is
// installed, which is the only state the settings surface addresses.
// ---------------------------------------------------------------------------
/// The effective raster-tile caching terms of the installed cache
/// (`MapSdk::raster_tile_cache_terms`'s return), shaped so a developer can
/// display the caching posture: the default, the provider's ceiling and the
/// value currently in force.
dictionary TileCacheTerms {
/// Time-to-live currently in force on the installed cache, in seconds:
/// the category default, or the last accepted override (clamped to the
/// ceiling when the override exceeded it).
u64 effective_time_to_live_seconds;
/// The installed provider's declared caching-terms ceiling in seconds,
/// or null when the provider declares no ceiling — the requested
/// time-to-live then applies verbatim. For the MapQuest raster tiles
/// this is the documented terms ceiling (thirty calendar days).
u64? provider_terms_ceiling_seconds;
/// The cache category's default time-to-live for raster tiles, in
/// seconds (thirty days by the owner ruling of 2026-10-28, PRD §6.7);
/// the value a freshly registered cache starts at.
u64 default_time_to_live_seconds;
};
// ---------------------------------------------------------------------------
// Navigation wiring (M6). The `[Trait, WithForeign]`
// `NavigationStateEventListener` is the listener contract foreign code
// implements; its subscriptions register on the `MapNavigationController`
// object, which drives the native `map-navigation` reducer session and its
// per-topic broadcaster. The records and enums below mirror the
// `map-navigation` plain-data types field-for-field; the step-advance and
// deviation policies stand in for the native decision objects (trait
// objects), which cannot cross the FFI, as tagged records. Payload-carrying
// native enums mirror as a kind enum plus nullable fields; the instruction
// payload mirrors without the native active maneuver's lane-guidance list,
// which stays native until the frozen route records carry lane guidance.
// ---------------------------------------------------------------------------
/// State-listener contract implemented by foreign code and subscribed on
/// `MapNavigationController` through the `subscribe_to_*` methods; each
/// registered subscription delivers only its own topic.
[Trait, WithForeign]
interface NavigationStateEventListener {
/// Receives the composed instruction payload of every dispatched state
/// change (the instructions topic).
void on_navigation_instruction(NavigationInstruction instruction);
/// Receives a deviation-status stream update (the deviation topic):
/// off-route on entry and on every recorded measurement update while the
/// deviation persists, on-route when it clears.
void on_deviation_status_change(DeviationStatusChange deviation_status_change);
/// Receives one phase transition per dispatched trip-state transition
/// (the phase topic).
void on_navigation_phase_change(NavigationPhaseChange navigation_phase_change);
/// Round 10 (speech pacing, OQ5's SDK-owned triggers): receives one
/// utterance trigger when the controller's distance-band policy judges
/// the fix newsworthy (a trigger band crossed this fix) — the verbatim
/// composed speech text, never rewritten, so the platform owns only
/// playback. Subscribers of the speech topic receive exactly the
/// `subscribe_to_speech_instructions` deliveries.
void on_speech_instruction(SpeechInstruction speech_instruction);
};
/// Handle coupled to exactly one `(topic, listener)` subscription; cancelling
/// is explicit and idempotent, dropping the handle does not unsubscribe.
interface NavigationTopicSubscription {
/// Cancels this subscription: the listener stops receiving the
/// subscription's topic from the next dispatch on. Idempotent.
void cancel();
};
/// The exported navigation controller: drives the native navigation reducer
/// session, broadcasts every state change to the registered listeners, and
/// answers read-only state and instruction queries. Obtain routes from
/// `MapSdk::calculate_route` and start a trip on them with this controller.
///
/// The listener callbacks run synchronously on the thread that drives the
/// controller call; a listener calling back into the controller while a
/// dispatch is still in flight is suppressed by the broadcaster's
/// single-in-flight guard (the state change behind it is already applied,
/// only its broadcast is skipped). The controller never holds its state lock
/// across a listener callback.
interface MapNavigationController {
/// Builds the controller with no active trip and no subscriptions.
constructor();
/// Subscribes the listener to the instructions topic: the composed
/// instruction payload of every dispatched state change, in registration
/// order.
NavigationTopicSubscription subscribe_to_navigation_instructions(NavigationStateEventListener listener);
/// Subscribes the listener to the deviation-status topic, in registration
/// order.
NavigationTopicSubscription subscribe_to_deviation_status(NavigationStateEventListener listener);
/// Subscribes the listener to the phase-transition topic, in registration
/// order.
NavigationTopicSubscription subscribe_to_phase_changes(NavigationStateEventListener listener);
/// Begins navigating `route` with the given policies, replacing any
/// active trip. The initial instruction payload is dispatched; no
/// deviation or phase transitions exist yet. A route without traversable
/// steps represents an already-arrived trip. Refuses policies with
/// non-finite or negative thresholds, and route records carrying a step
/// maneuver label the conversion cannot map.
[Throws=SdkError]
void start_trip(RouteData route, UnitSystem unit_system, StepAdvancePolicy step_advance_policy, DeviationPolicy deviation_policy);
/// Delivers one position fix to the active trip; without one the fix is
/// dropped. Fixes without an accuracy report never trigger a step
/// advance and are never judged by the deviation detector.
void provide_location_update(NavigationLocationUpdate location_update);
/// Replaces the route of the active trip (a reroute). The replacement
/// route must be anchored at the trip's current snapped coordinate; the
/// step advance strategy for the new route is configured by
/// `step_advance_policy` and the deviation policy persists. A reroute
/// offered to an arrived trip is ignored (no transition); without an
/// active trip the event is dropped. Round 11: an in-flight reroute
/// calculation is settled by this call (applied or ignored alike), so
/// the request-in-flight gate reopens after the integrator acts.
[Throws=SdkError]
void provide_reroute(RouteData route, StepAdvancePolicy step_advance_policy);
/// Round 11 (Ferrostar B3): applies the reroute-recalculation gates to
/// one reroute-request signal at the wall clock: true when every gate
/// passed (no calculation in flight, outside the cooldown window, and
/// — when both fix references exist — beyond the movement threshold)
/// and a calculation is now in flight, readable as the trip state's
/// `is_calculating_new_route`; false when the signal was refused. A
/// signal without an open, still-navigable trip (no trip, or the trip
/// has arrived) is refused before the gates are consulted. A passing
/// caller owes a settle: the eventual `provide_reroute` (applied or
/// ignored) or `cancel_reroute_calculation`. The initial (trip's first)
/// attempt always passes the cooldown and movement gates.
boolean begin_reroute_calculation();
/// Round 11: settles an in-flight reroute calculation without applying a
/// route (the integrator abandoned the request): the request-in-flight
/// gate reopens. The cooldown keeps running from the calculation's
/// beginning and the movement reference stays where it was. Idempotent.
void cancel_reroute_calculation();
/// Round 11: swaps the reroute-recalculation hysteresis policy in force,
/// keeping the gate's memory as it stands. Both thresholds must be
/// finite and non-negative (refused with `InvalidNavigationInput`
/// otherwise, and the policy in force is then untouched).
[Throws=SdkError]
void configure_reroute_policy(ReroutePolicy reroute_policy);
/// The plain state of the active trip, or null without one.
TripNavigationState? current_trip_state();
/// The composed turn-by-turn instruction payload of the current trip
/// state, or null without an active trip; repeated reads of an unchanged
/// state return the same payload.
NavigationInstruction? current_instructions();
/// Round 10 (speech pacing, OQ5): subscribes the listener to the
/// speech-instruction trigger topic, in registration order. Each fix's
/// dispatch consults the controller's named-potential policy: the trigger
/// bands reuse the display phrase's distance bands (so a spoken "in 100
/// meters" always matches the payload's shown phrase band), each band
/// announces at most once per step (monotone progress), and a minimum
/// inter-utterance spacing and repeat distance suppress chatter. The
/// platform keeps playback (including its fallback speak-latest behavior
/// when no listener subscribes) and synthesis stays out of scope.
NavigationTopicSubscription subscribe_to_speech_instructions(NavigationStateEventListener listener);
};
/// High-level phase of a trip that has already been started. Rerouting is not
/// a phase of its own: the machine flows Navigating ⇄ OffRoute within a trip
/// and Navigating → Arrived at its end.
enum NavigationPhase {
/// Following the route's step sequence.
"Navigating",
/// The deviation detector judged the last graded fix off route; progress
/// is frozen at the last on-route values until a graded fix recovers or a
/// reroute replaces the route.
"OffRoute",
/// The final step has been completed; location updates no longer change
/// the state.
"Arrived",
};
/// Which grade of deviation a recorded deviation carries: the two-grade
/// distinction between off the current step yet still on the route line and
/// completely off the route. Only the fully-off grade freezes navigation; the
/// wrong-step grade never freezes on its own.
enum DeviationGradeKind {
/// The fix projects onto neither the current step nor any future step of
/// the remaining route line: the freezing grade. The trip's progress is
/// frozen until a graded fix recovers or a reroute replaces the route.
"FullyOffRoute",
/// The fix is off the current step but projects onto a future step of the
/// remaining route line: "on route, wrong step". The trip keeps
/// navigating and counting down, and is never frozen by this grade alone.
"OffStepOnRoute",
};
/// Plain-data description of an active deviation from the route. The grade
/// kind distinguishes the freezing fully-off grade from a merely wrong-step
/// record: a wrong-step record is guidance feedback ("on the route, on a
/// later step") carried while the trip phase stays `Navigating`.
dictionary DeviationInfo {
/// Which grade of deviation this is (see `DeviationGradeKind`).
DeviationGradeKind grade_kind;
/// Distance, in meters, from the raw fix that triggered (or most recently
/// confirmed) the deviation to the remaining route line.
double deviation_from_route_meters;
};
/// Location of one route step in traversal order across a route's legs.
dictionary RouteStepPosition {
/// Index of the leg that owns this step in the route's legs.
u64 leg_index;
/// Index of the step within its leg in the leg's steps.
u64 step_index;
};
/// Progress along the remaining trip.
dictionary TripProgress {
/// Distance, in meters, from the snapped position to the end of the
/// current step — the point of the next maneuver.
double distance_to_next_maneuver_meters;
/// Total remaining distance, in meters, including the current step's
/// remainder.
double distance_remaining_meters;
/// Total remaining travel time, in seconds, measured the same way as
/// `distance_remaining_meters`.
double duration_remaining_seconds;
};
/// The full navigable state of an active trip, read-only for foreign code.
/// The route's steps are carried as traversal-ordered indices, never
/// duplicated; the live step-advance condition and deviation detector are not
/// records and cannot cross the FFI — they are configured through the
/// navigation policies on the controller.
dictionary TripNavigationState {
/// Current phase of the trip.
NavigationPhase phase;
/// The route being navigated.
RouteData route;
/// All steps across every leg, flattened in traversal order.
sequence<RouteStepPosition> step_positions;
/// Index into `step_positions` of the step currently traversed.
u64 current_step_position;
/// Progress along the remaining trip.
TripProgress progress;
/// Position snapped onto the current step's shape; null until the first
/// location update has been processed.
GeographicCoordinate? snapped_coordinate;
/// Active deviation, as judged by the deviation detector; a fully-off
/// grade is set exactly while the phase is `OffRoute`, and a wrong-step
/// (`OffStepOnRoute`) grade may be set while the phase stays `Navigating`.
DeviationInfo? deviation;
/// Round 11 (Ferrostar B3): whether a reroute recalculation is currently
/// in flight. This is the controller's wall-clock gate fact, not reducer
/// state — the reducer stays clock-free — so it is read fresh from the
/// controller on every readback.
boolean is_calculating_new_route;
};
/// Length unit a distance-phrase magnitude is expressed in.
enum DistanceUnit {
/// Meters, used by the metric near and moderate bands.
"Meters",
/// Kilometers, used by the metric far band.
"Kilometers",
/// Feet, used by the imperial near and moderate bands.
"Feet",
/// Miles, used by the imperial far band.
"Miles",
};
/// Which form a distance phrase takes.
enum DistancePhraseKind {
/// The position is closer than half a near-band increment to the maneuver
/// point; no distance is phrased and composed text drops the prefix. The
/// magnitude is null on this kind.
"Immediate",
/// A rounded distance magnitude in a specific unit; the magnitude and
/// unit are set on this kind.
"Quantity",
/// No distance is phrased because the freezing (fully-off-route) deviation
/// grade holds: the projection is frozen, so any countdown composed off it
/// would be a phantom measurement. The magnitude is null on this kind, and
/// the composed text channels carry no distance prefix.
"Suppressed",
};
/// The unit-system-aware phrasing of the distance remaining to a maneuver,
/// rounded into the named distance phrasing bands.
dictionary DistancePhrase {
/// Which form the phrase takes.
DistancePhraseKind phrase_kind;
/// Rounded magnitude of the phrase; null for the immediate and suppressed
/// forms.
double? magnitude;
/// Unit the magnitude is expressed in; null for the immediate and
/// suppressed forms.
DistanceUnit? magnitude_unit;
};
/// Which form an instruction text takes.
enum ManeuverInstructionTextKind {
/// The provider authored instruction text for this maneuver.
"Composed",
/// The provider authored no instruction text for this maneuver; nothing
/// is invented to fill it. All four text fields are null on this kind.
"Unavailable",
};
/// The composed text channels of one maneuver: provider-authored text is
/// carried verbatim beside the composed strings.
dictionary ManeuverInstructionText {
/// Which form the text takes.
ManeuverInstructionTextKind text_kind;
/// Provider-authored display text, verbatim and never rephrased.
string? provider_display_text;
/// Provider-authored speech text, verbatim and never rephrased.
string? provider_speech_text;
/// Banner variant: the live distance prefix plus the display variant.
string? composed_display_text;
/// Text-to-speech variant: the live distance prefix plus the speech
/// variant.
string? composed_speech_text;
};
/// Instruction payload of the maneuver currently being traversed (the banner
/// maneuver: the step the snapped position sits inside, with the distance
/// measured to that step's end — the maneuver point).
dictionary ActiveManeuverInstruction {
/// Composed display and speech text of the active step.
ManeuverInstructionText instruction;
/// Unit-system-aware distance phrasing of the distance to the next
/// maneuver.
DistancePhrase distance_phrase;
/// Structured exit index of a roundabout active step, starting at one.
u8? roundabout_exit_index;
};
/// Instruction payload of the maneuver after the active one (the "then"
/// instruction), text-carrying only.
dictionary UpcomingManeuverInstruction {
/// Composed display and speech text of the following step.
ManeuverInstructionText instruction;
/// Structured exit index when the following step is a roundabout.
u8? roundabout_exit_index;
};
/// The emitted turn-by-turn instruction payload for one trip state; the same
/// state yields the same payload on every read.
dictionary NavigationInstruction {
/// The banner maneuver currently being traversed.
ActiveManeuverInstruction active_maneuver;
/// The maneuver after the active one, where the route has a following
/// step (always null on the route's final step and after arrival).
UpcomingManeuverInstruction? upcoming_maneuver;
/// True exactly while the trip has arrived and the remaining progress is
/// zero.
boolean is_arrival;
/// True exactly while the trip is off route: the payload beneath the
/// marker is the frozen last-on-route projection, whose distance phrase
/// is suppressed rather than presented, and must not be presented as
/// current guidance.
boolean is_rerouting_required;
};
/// Which form a deviation-status update takes.
enum DeviationStatusChangeKind {
/// The trip is judged deviating; the deviation description is set on this
/// kind. The description's grade kind distinguishes the freezing
/// fully-off grade from a merely wrong-step record, which is delivered
/// while the trip phase stays `Navigating`.
"OffRoute",
/// A previously recorded deviation cleared; the trip is judged on route.
"OnRoute",
};
/// A deviation-topic update: the detector's judgment changed between the
/// previous and the current trip state — entry, measurement drift while the
/// deviation persists, and clearing.
dictionary DeviationStatusChange {
/// Which form the update takes.
DeviationStatusChangeKind status_change_kind;
/// The recorded measurement of the deviation, set exactly on the
/// off-route kind.
DeviationInfo? deviation_info;
};
/// A phase-transition event delivered on the phase topic.
enum NavigationPhaseChange {
/// The trip entered the off-route phase from the navigating phase.
"OffRouteEntered",
/// A graded fix back on the route line lifted the deviation and returned
/// the trip to the navigating phase.
"OffRouteCleared",
/// A reroute event replaced the route (delivered only when the event
/// actually applied — an ignored reroute produces no transition).
"RerouteApplied",
/// The final step completed; the trip reached the arrived phase.
"Arrived",
};
/// One raw position fix delivered to the navigation controller.
dictionary NavigationLocationUpdate {
/// Raw position on the WGS 84 ellipsoid, before route snapping.
GeographicCoordinate coordinate;
/// Radius, in meters, of the 68% horizontal-accuracy envelope of this fix
/// (null when the source does not report accuracy).
double? horizontal_accuracy_meters;
/// Direction of travel in degrees clockwise from true north (null when
/// the source does not report a course).
double? course_heading_degrees;
/// Fix instant in seconds since the Unix epoch (null for untimed
/// sources).
double? timestamp_seconds;
};
/// Which step-advance strategy a navigation policy configures.
enum StepAdvancePolicyKind {
/// Steps change only when driven programmatically (not available on this
/// surface yet); the trip still tracks progress. The threshold fields are
/// ignored on this kind.
"Manual",
/// Advances when the position fix comes within the threshold of the end
/// of the current step, eager-advance semantics.
"DistanceToEndOfStep",
/// Advances when the position fix moves farther from the current step's
/// polyline than the threshold, delayed-advance semantics.
"DistanceFromStep",
};
/// The step-advance policy of a trip, standing in for the native decision
/// object that cannot cross the FFI.
dictionary StepAdvancePolicy {
/// Which step-advance strategy the policy configures.
StepAdvancePolicyKind policy_kind;
/// Distance, in meters, of the advance threshold (the distance to the
/// step's end, or from the step's polyline, depending on the kind).
double advance_threshold_meters;
/// Fixes with a reported horizontal accuracy worse than this, in meters,
/// never advance.
double minimum_horizontal_accuracy_meters;
};
/// Which deviation-detection strategy a deviation policy configures.
enum DeviationPolicyKind {
/// Flags a deviation when the raw fix is farther from the remaining route
/// line than the configured threshold, widened by the fix's reported
/// accuracy.
"DistanceThreshold",
};
/// The deviation-detection policy of a trip, standing in for the native
/// decision object that cannot cross the FFI.
dictionary DeviationPolicy {
/// Which deviation-detection strategy the policy configures.
DeviationPolicyKind policy_kind;
/// Distance, in meters, from the remaining route line beyond which a fix
/// is judged off route (before any accuracy widening, strictly greater).
double deviation_threshold_meters;
};
// ---------------------------------------------------------------------------
// Round 11 (Ferrostar adoption batch 2, mechanism adopted from `FerrostarCore`
// under its BSD 3-Clause license; the expressions are our own). The
// reroute-recalculation hysteresis: three gates stand between a fully
// off-route signal and an actual recalculation request — the request-in-flight
// gate, the cooldown and the movement threshold — plus the in-flight
// readback on the trip state, so a jittery off-route bounce cannot spam the
// integrator's routing engine. The gates live at the controller seam with
// the wall clock (exactly like the speech pacing's planner), never in the
// reducer: the reducer stays a pure, clock-free event function. The gates
// close the round-9 contract gap ("no reroute timestamp on the phase
// channel") without an addition there: a reroute timestamp on the phase
// channel is not addition-only (`NavigationPhaseChange` stays a payloadless
// flat enum), so the phase channel stays untouched and the honest closure is
// the gate seam plus the `is_calculating_new_route` readback plus
// `begin_reroute_calculation`'s boolean answer.
// ---------------------------------------------------------------------------
/// The reroute-recalculation hysteresis policy of a navigation controller
/// (round 11, Ferrostar B3's mechanism). Both thresholds must be finite and
/// non-negative; the controller refuses otherwise (`InvalidNavigationInput`),
/// leaving the policy in force untouched.
dictionary ReroutePolicy {
/// Minimum wall-clock seconds between the beginnings of two reroute
/// calculations (the SDK default: 5).
double recalculation_cooldown_seconds;
/// Minimum displacement, in meters, between the raw position fix current
/// at a calculation's beginning and the one current at the previous
/// calculation's beginning (the SDK default: 50).
double recalculation_movement_meters;
};
// ---------------------------------------------------------------------------
// Native surface views (round 8, slice 0). The additions below mirror the
// wasm canvas' overlay plain-object names — `routePolyline`,
// `filledPolygon`, `pointMarkers`, `trafficFlow` — as a kind enum plus
// nullable fields (the round-7 navigation payload pattern: a field-carrying
// native enum lower as a tag with `null` marking the unused payload
// fields), and expose one `MapSurfaceView` object: the FFI face of the
// `map-rendering` surface renderer, drawn by a platform view the foreign
// code owns (Kotlin `SurfaceView`, Swift `UIView`/`MKMapView`-style view,
// PRD §4.4–4.6). The renderer itself stays native (same `wgpu` machinery as
// the headless capture path); the FFI surface carries the lifecycle
// (attach/resize/detach), the overlay list, and an explicit per-frame draw.
// The platform's own render cadence drives the view — `draw` is per-frame,
// called from a display link or choreographer callback, never an SDK-
// internal loop: no SDK thread this slice, documented below.
// ---------------------------------------------------------------------------
/// Which overlay shape one descriptor configures. The kind tags follow the
/// wasm canvas' plain-object `kind` strings (the wasm seam passes the
/// lowercase camel kinds `routePolyline`/`filledPolygon`/`pointMarkers`/
/// `trafficFlow`; the FFI enum variants are the contract's own CamelCase),
/// mirroring the native `MapOverlay` payloads one to one.
enum MapOverlayKind {
/// A routed path drawn as a thick line.
"RoutePolyline",
/// A filled polygon: one outer ring, ear-clip triangulated.
"FilledPolygon",
/// Screen-facing point markers (octagon fans).
"PointMarkers",
/// Congestion-colored traffic-flow lines.
"TrafficFlow",
};
/// Solid RGBA overlay style, every channel in `[0, 1]`. Mirrors the wasm
/// canvas' `color` plain object. Null style fields keep the documented
/// `map-rendering` default style of the matching overlay kind.
dictionary OverlayColor {
/// Red channel in `[0, 1]`.
double red;
/// Green channel in `[0, 1]`.
double green;
/// Blue channel in `[0, 1]`.
double blue;
/// Alpha channel in `[0, 1]` (the overlay pipeline alpha-blends).
double alpha;
};
/// One overlay shape drawn over the map surface's tiles in every frame,
/// selected by `kind`. Each kind reads exactly its own payload fields and
/// ignores the others; a descriptor whose kind-required payload is absent
/// (or whose style field is unusable) rejects the whole `set_overlays` call
/// and keeps the previous list in force — the wasm canvas' all-or-nothing
/// semantics. Absent style fields keep the documented map-rendering
/// default style; present style fields must be usable (channels in
/// `[0, 1]`, widths and radii positive and finite). The record mirrors the
/// native `map-rendering` overlay structs field-for-field; the wasm canvas
/// plain objects carry the same names in camel case (`ringCoordinates`,
/// `pointMarkerPositions`, `pointMarkerRadiusPixels`,
/// `trafficFlowSegments`).
dictionary MapOverlayDescriptor {
/// Which overlay shape this descriptor configures and which payload
/// fields carry it.
MapOverlayKind kind;
/// `RoutePolyline`: the traversal-order polyline; consecutive equal
/// coordinates skip their degenerate segment. Required for the kind.
sequence<GeographicCoordinate>? coordinates;
/// Route polyline, filled polygon and point markers: solid color
/// override, or null for the kind's documented default.
OverlayColor? rgba_color;
/// Route polyline and traffic flow: line width in screen pixels
/// (positive and finite), or null for the kind's documented default.
double? width_pixels;
/// `FilledPolygon`: the outer ring in traversal order without a
/// repeated closing vertex (a repeated one is tolerated and dropped).
/// Required for the kind.
sequence<GeographicCoordinate>? ring_coordinates;
/// `PointMarkers`: the geographic anchor of every marker. Required for
/// the kind.
sequence<GeographicCoordinate>? point_marker_positions;
/// Point markers: marker radius in screen pixels (positive and finite),
/// or null for the documented default.
double? point_marker_radius_pixels;
/// `TrafficFlow`: the flow observations to paint, one per road segment
/// (or polyline of segments), colored by `congestion_level` — the same
/// `TrafficFlowData` records the `fetch_traffic_flow` call returns.
/// Required for the kind.
sequence<TrafficFlowData>? traffic_flow_segments;
};
/// The platform window surface handle foreign code hands to
/// `MapSurfaceView::attach`: exactly one platform pointer is set, the rest
/// stay null (a null-or-ambiguous handle is refused with
/// `SdkError::InvalidRenderInput`; a null pointer is `u64 0` or null, never
/// a zeroed pointer passed through). The handle shape is a single record
/// with one nullable pointer per supported platform — one dictionary keeps
/// add-a-platform growth additive and mirrors how uniffi records lower
/// everywhere. The object behind the pointer must outlive the attached
/// view: the raw handles are the caller's validity guarantee (see
/// `wgpu::SurfaceTargetUnsafe`), and detach/drop releases the Rust side
/// first, so dropping the platform view before `detach` is the caller's
/// bug.
dictionary MapSurfaceTargetHandle {
/// macOS: pointer to the `NSView` the surface renders into (Metal
/// creates the layer from the view); null on the other platforms.
u64? appkit_view_pointer;
/// iOS: pointer to the `UIView` the surface renders into; null on the
/// other platforms.
u64? ios_view_pointer;
/// Android: pointer to the `ANativeWindow` obtained from the platform
/// `Surface` (`ANativeWindow_fromSurface`); null on the other platforms.
u64? android_native_window_pointer;
};
/// The native map surface view: renders the camera-driven tiled map plus
/// the overlay list onto a live platform window surface through the same
/// `wgpu` machinery as the headless capture path. Construct with the
/// `MapSdk` whose registered raster-tile provider the attached surface
/// draws from (the provider resolves through the registry at attach time —
/// with no registered provider, `attach` throws
/// `SdkError::ProviderNotConfigured`).
///
/// # Lifecycle
///
/// `attach` wraps the platform handle, acquires the surface-compatible
/// graphics context, negotiates the surface format, configures the initial
/// extent and resolves the tile provider — every step's failure throws
/// (`SdkError::RenderingSurfaceUnavailable` for the GPU/surface path,
/// `InvalidRenderInput` for a bad handle or extent, `ProviderNotConfigured`
/// for the missing provider slot). `resize` (explicit, called from the
/// platform's surface-size callback) reconfigures the surface; a zero
/// extent rejects. `detach` releases the surface state until a re-attach.
/// Overlay state outlives attach/detach cycles: `set_overlays` and
/// `clear_overlays` work detached, and the next attach draws the stored
/// list.
///
/// # Draw model
///
/// Frames are explicit: the foreign platform view calls `draw` once per
/// rendered frame from its own cadence (a display link, a choreographer, a
/// resize-then-once pattern) with the current `MapCamera` value — there is
/// deliberately no SDK-internal render thread or continuous redraw loop in
/// this slice. Each draw lists the camera's visible tiles, fetches the
/// pooled misses from the surface-attached provider (budgeted per frame,
/// parent fallback like the headless path), and renders tiles then
/// overlays alpha-blended, presenting to the surface. A failed frame
/// throws and leaves the previous frame on screen. Draws on one view are
/// serialized: a draw holding the view's render state (the fetch phase
/// spans network await) reports a concurrent `draw` or `resize` with
/// `RenderingSurfaceUnavailable`, the foreign cadence retrying the next
/// frame.
interface MapSurfaceView {
/// Builds the view over the SDK state it draws from.
constructor(MapSdk map_sdk);
/// Attaches the platform surface of the given initial physical-pixel
/// extent (the extent must be non-zero; a platform view not laid out
/// yet waits for `resize`). The wrap validates the handle, acquires
/// the context, negotiates the format, configures and resolves the
/// raster provider. Attaching an attached view is refused.
[Throws=SdkError]
void attach(MapSurfaceTargetHandle surface_handle, u32 width_pixels, u32 height_pixels);
/// Reconfigures the surface to the new physical-pixel extent (both
/// non-zero); a mid-draw resize surfaces the busy state and the next
/// frame's draw or the next resize call picks it up.
[Throws=SdkError]
void resize(u32 width_pixels, u32 height_pixels);
/// Detaches: releases the surface, its renderer and its scheduler.
/// Overlay state persists; re-attach rebuilds the drawing machinery.
/// Safe against a draw still in flight (its frame completes and the
/// state is then dropped without being restored).
void detach();
/// Replaces the overlay list drawn over the tiles in every following
/// `draw`, replacing any earlier list in order (later entries draw
/// over earlier ones); an empty list clears. Every descriptor must be
/// valid for its kind: no partial lists are kept.
[Throws=SdkError]
void set_overlays(sequence<MapOverlayDescriptor> overlay_descriptors);
/// Clears the overlay list; following frames draw the tiles alone
/// until `set_overlays` supplies a new list.
void clear_overlays();
/// Draws one frame for the camera: the camera's visible tiles fetched
/// and rendered, overlaid, presented. Returns the frame's tile and
/// overlay statistics (round-9 ask) so the platform render loop can
/// report exactly what a frame drew: drawn pixels this frame come from
/// the present and fallback tiles, while the deferred and unavailable
/// counts are where later frames may still change pixels. Throws when
/// the view is detached, a draw is already in flight, or the frame's
/// rendering failed; a thrown draw leaves the previous frame on screen.
[Async, Throws=SdkError]
TileFrameStatistics draw(MapCamera camera);
/// Round-10 (renderer-owned location indicator): sets the single
/// location layer drawn after the tile pass and the overlay pass
/// (topmost SDK layer), replacing any earlier state — mirroring
/// `set_overlays`' all-or-nothing semantics, so a state or style that
/// fails validation throws and keeps the earlier indicator. The state
/// outlives attach/detach cycles like the overlay list; the styled
/// visuals resolve to the documented map-rendering defaults for every
/// null style field.
[Throws=SdkError]
void set_location_indicator(LocationIndicatorState state, LocationIndicatorStyle style);
/// Clears the location layer; following frames draw tiles and overlays
/// alone until `set_location_indicator` supplies new state.
void clear_location_indicator();
/// Round-10 (idle-settled redraw discipline): reads whether the view's
/// presented state would not change if `draw` ran again right now —
/// true when this camera value equals the last drawn camera, the overlay
/// list and the location indicator are unchanged since that draw, and
/// the last frame's tile statistics resolved no pending pixels (no
/// fallback, deferred or unavailable tiles). A pure query: it never
/// draws, fetches or mutates state, so a platform render pump can call
/// it every display frame and skip `draw` when it answers true, letting
/// the presented frame stand and resuming on any change or on the next
/// pending detail's arrival. Throws when the view is detached.
[Throws=SdkError]
boolean frame_settled(MapCamera camera);
};
/// One presented frame's tile and overlay accounting, mirroring how the
/// surface renderer resolves its scheduler verdicts. Every requested tile
/// lands in exactly one of the four readiness counts, so
/// `present + fallback + deferred + unavailable` always equals `requested`.
/// The four readiness names mirror the tile scheduler's fetch-state
/// taxonomy (`Present`/`FallsBackTo`/`Deferred`/`Unavailable`).
dictionary TileFrameStatistics {
/// Tile requests the frame resolved (per request; a request listing
/// carrying duplicates costs one fetch but reports per request).
u32 requested_tile_count;
/// Tiles drawn at their own zoom level.
u32 present_tile_count;
/// Tiles drawn from their nearest pooled ancestor.
u32 fallback_tile_count;
/// Tiles whose fetch was deferred by the per-frame budget.
u32 deferred_tile_count;
/// Tiles the provider rejected this frame.
u32 unavailable_tile_count;
/// Clip-space triangles the overlay layer contributed to the frame.
u32 overlay_triangle_count;
};
// ---------------------------------------------------------------------------
// Round-10 contract additions (addition-only; renderer-owned location
// indicator, SDK-owned speech triggers, structured diagnostics).
// ---------------------------------------------------------------------------
/// Which visual form the renderer-owned location layer draws (OQ3's
/// style-selected single layer).
enum LocationIndicatorVisualKind {
/// Plain disc (tracking mode: no course known or no route).
"TrackingDot",
/// Disc plus course-directed heading wedge (navigation follow mode).
"GuidedCourseArrow",
};
/// Style of the location layer; null fields keep the documented
/// `map-rendering` defaults (the overlay-style pattern). The disc and wedge
/// are screen-sized pixel constants (a position marker scales with the
/// screen, not the ground); the accuracy ring is ground-sized.
dictionary LocationIndicatorStyle {
/// Which visual form this layer draws.
LocationIndicatorVisualKind visual_kind;
/// Disc radius in screen pixels; null keeps the default.
double? radius_pixels;
/// Disc fill color; null keeps the default.
OverlayColor? rgba_color;
/// Accuracy ring drawn around the disc (shown only when a fix carries a
/// reported accuracy); null keeps the default (shown whenever the fix
/// reports one that reaches the minimum draw size).
boolean? show_accuracy_ring;
};
/// One frame's location-layer state (the presentable subset of a position
/// fix): position, optional course over ground, optional horizontal accuracy.
dictionary LocationIndicatorState {
/// Fix position.
GeographicCoordinate coordinate;
/// Course over ground in degrees clockwise from true north; null when
/// the source does not report one (the wedge renders only when set and
/// the style carries `GuidedCourseArrow`).
double? course_heading_degrees;
/// Horizontal accuracy radius in meters; the accuracy ring renders when
/// set and the style shows rings.
double? horizontal_accuracy_meters;
};
/// When a spoken-trigger event fires and what carries it (OQ5). The
/// utterance text is the contract's composed speech text verbatim — the SDK
/// owns when a line is newsworthy, the platform owns playback and synthesis.
dictionary SpeechInstruction {
/// The verbatim speech text (from the contract's composition; never
/// rewritten by the trigger policy).
string utterance_speech_text;
/// The live distance to the maneuver in meters at emission time.
double trigger_distance_meters;
/// Round 11 (Ferrostar B4): the utterance identity — a stable monotonic
/// id composed at emission, strictly increasing over every line the
/// controller has emitted since construction and never reused.
/// Identical text re-emitted is a new id; dedup and flush decisions stay
/// at the platform speaker.
u64 utterance_id;
/// What triggered this line (a crossed band, the immediate band, an
/// off-route recovery, or the trip's arrival).
SpeechInstructionKind instruction_kind;
};
/// What triggered a `SpeechInstruction`.
enum SpeechInstructionKind {
/// A trigger band was crossed this fix (the moderate or far threshold).
"ManeuverApproach",
/// The immediate band: the maneuver is imminent.
"ImmediateManeuver",
/// Guidance just recovered from off-route (guidance resumed).
"OffRouteRecovery",
/// The trip arrived.
"Arrival",
};
/// Severity of an SDK diagnostic event, mirroring the `tracing` levels the
/// crates log under.
enum SdkLogSeverity {
/// Very fine-grained frame-path instrumentation (GPU-hot path logs live
/// here, only when a listener subscribed at this severity).
"Trace",
/// Fine-grained internal state transitions.
"Debug",
/// Coarse-grained progress (the default minimum for a subscription).
"Info",
/// A problem this event explains.
"Warn",
/// A failure this event explains.
"Error",
};
/// One structured SDK internal log event, as delivered to a foreign
/// diagnostics listener or read back by a flush. Coordinates in event
/// messages are rounded at the sink (never the API key, and never raw
/// coordinates at full precision).
dictionary DiagnosticLogEvent {
/// Wall-clock milliseconds since the Unix epoch, as the ring recorded
/// the event.
u64 timestamp_milliseconds_since_epoch;
/// The event's severity.
SdkLogSeverity severity;
/// The named per-subsystem target that logged it, e.g. "tile_fetch".
string subsystem_target;
/// The one-line already-scrubbed message.
string message_line;
};
/// Diagnostics contract implemented by foreign code, receiving the SDK's
/// structured internal log events fire-and-forget (the Mozilla
/// application-services ADR 0009 sink pattern): the SDK copies an event
/// across the FFI and moves on immediately without awaiting the listener —
/// a slow listener can never slow the events it observes, and a frame-path
/// event never blocks on a foreign callback.
[Trait, WithForeign]
interface SdkDiagnosticEventListener {
/// Receives one structured event (severity-gated at the subscription).
void on_diagnostic_event(DiagnosticLogEvent event);
};
// ---------------------------------------------------------------------------
// Round-12 provider-failure diagnostics (addition-only; the owner ruling of
// 2026-10-29 on unclassified foreign provider failures). Foreign
// implementations of the `[Trait, WithForeign]` provider contracts fail in
// two shapes: the typed shape (the foreign code threw one of the contract's
// `SdkError` variants, which round-trips one-to-one) and the unclassified
// shape (the foreign code failed some other way, which the Rust conversion
// reports under `ProviderRequestRejected` with its verbatim reason — the
// honest but thin detail the seam exists for). Every failed foreign-callback
// provider call is recorded on the seam with the contract and the callback
// method it occurred on, a timestamp, the classification, and the verbatim
// failure text; `MapSdk::latest_provider_failure_diagnostics` reads the
// bounded ring back.
//
// Honest limitation of the classification: the unclassified shape is
// recognized by the unclassified-callback conversion's verbatim detail text
// (one Rust-side constant shared by the conversion and the adapter-side
// classifier), so a foreign implementation that itself throws
// `ProviderRequestRejected` carrying exactly that conversion phrasing is
// indistinguishable and lands as unclassified. Richer typed payloads (a
// provider status code, a retriable flag) would need a channel the frozen
// provider traits cannot carry without a contract change; until one exists
// the foreign side's only input to the seam is the failure text it throws,
// which the entries carry verbatim — no data is parsed or invented.
// ---------------------------------------------------------------------------
/// Which shape of foreign provider failure a diagnostic captured
/// (`ProviderFailureDiagnostic::failure_classification`).
enum ProviderFailureClassification {
/// The foreign implementation failed without a mapped error (an
/// exception the contract does not spell): the failure text is the
/// uniffi callback machinery's verbatim reason for it.
"UnclassifiedCallbackFailure",
/// The foreign implementation threw one of the contract's `SdkError`
/// variants, which round-trips typed through the seam: the failure text
/// is the thrown binding error's verbatim display form.
"MappedContractError",
};
/// One failed foreign-callback provider call, as retained in
/// `MapSdk::latest_provider_failure_diagnostics`'s bounded per-object ring.
/// Everything carried here is knowable at the adapter boundary that bridges
/// the foreign implementation onto the native provider registry; no data is
/// invented beyond it.
dictionary ProviderFailureDiagnostic {
/// Wall-clock milliseconds since the Unix epoch, as the seam recorded
/// the failure.
u64 timestamp_milliseconds_since_epoch;
/// Which failure shape this entry captured.
ProviderFailureClassification failure_classification;
/// The provider contract the foreign code implements, e.g.
/// `GeocodingProvider`.
string provider_contract_name;
/// The contract method whose call failed, e.g. `forward_geocode`.
string provider_method_name;
/// The verbatim failure text: the uniffi callback machinery's reason for
/// the unclassified shape, or the thrown binding error's display form
/// for the mapped shape.
string failure_detail;
};
Contributing Conventions — Rustic Maps SDK
The project guides set the repository’s behavioral rules. This document translates them into per-language practice, including the generated binding surfaces.
1. No abbreviations in public identifiers
Every public identifier is spelled out, and stays concise yet precise:
| Good | Bad |
|---|---|
GeographicCoordinate | GeoCoord |
RouteStep | RStep |
instruction_display_text | instrDispTxt |
fetch_traffic_incidents | fetchTI |
time_to_live_seconds | ttl |
The standard forms SDK, URL, HTTP, wasm, and MVT are accepted
in documentation text, but never as identifier shorthand.
2. SOLID mapping
| Principle | Rust core | Kotlin binding surface | Swift binding surface |
|---|---|---|---|
| Single responsibility | one crate per concern | one class per screen/feature | one struct per screen/feature |
| Open–closed | a new vendor means a new *Provider impl; the core stays untouched | a new capability means a new composable or view-model | final class + protocol; no forks in existing code |
| Liskov substitution | mocks in geospatial-traits::mock must satisfy every tested consumer | subclasses and refapps honor suspend contracts | SwiftUI view models honor async throws contracts |
| Interface segregation | ten narrow contracts, not one mega-trait | expose role-targeted protocols, not god objects | role-targeted protocols |
| Dependency inversion | crates depend on geospatial-traits, never on a provider | view models depend on injected SDK protocols | dependency injection through initializers |
3. Errors
- Rust: every failure is
geospatial_types::SDKError. Crate code uses nounwrap, noexpect, and no rawStringerrors, and panics never cross the FFI boundary. - Kotlin: UniFFI maps the UDL contract to typed exceptions, and the refapps surface them as sealed UI states.
- Swift: UniFFI maps the contract to typed Swift errors thrown from async methods.
4. Property- and fixture-first testing
- Coordinate, camera, and projection math gets
proptestsuites. - Every external payload gets a recorded fixture and a parser test.
- Reference applications double as UI-test hosts (Espresso / XCUITest / Detox).
How the demo loop runs
How the WebAssembly harness behind the site’s future demo is built, served, and tested. This page stays deliberately short: it is the map of the loop, not its internals.
The three steps
- Build. The
map-web-bindingscrate compiles towasm32-unknown-unknownand thewasm-bindgenCLI emits the ES module the pages import. The CLI’s version and the crate’swasm-bindgendependency are a locked pair: one bump regenerates the emit. - Serve. A small dependency-free static server serves the
generated
pkg/plus a set of allow-listed same-origin routes: recorded raster-tile imagery, the six committed golden images, the shared reference-scenes demo artifact, and the compiled offline-routing archive. The browser never touches an external network; the loop is fully offline against recorded fixtures, and the camera’s default anchor sits inside the recorded tile neighborhood, so the page shows real imagery immediately. - Test. The Playwright suite runs headless over Chromium: version and projection probes, round-trip projection, antimeridian tile listing, a WebGPU canvas smoke, and camera readouts. A separate contract-sync test (no browser, no npm) parses the frozen UDL, the hand-reviewed TypeScript mirror, and the wasm exports, and asserts the three agree.
What keeps the parity honest
The browser canvas mirrors the native renderer with one rule: same WGSL text, same vertex layout, same index pattern. Tests render the same camera against committed golden reference images in both places, so a browser-vs-native pixel compare stays meaningful.
Known limitations
- The static server binds a fixed localhost port, so two harness sessions cannot share it; a test-only environment-variable port override is the workaround.
- The GPU canvas draws only through WebGPU. Its documented degradation path is a 2D overlay grid, never WebGL.
- WebKit runs resolve WebGPU nowadays, so a WebKit run exercises the GPU path, not the 2D fallback.