Skip to main content

maplibre_native/
lib.rs

1//! Safe Rust binding for the MapLibre Native C API.
2//!
3//! This crate owns Rust-specific ergonomics and safety policy: thread-affine
4//! public handles, parent retention, owner-thread `Drop`, Rust errors,
5//! callback closure APIs, and lifetime-scoped render resources. Shared C ABI
6//! adaptation lives in `maplibre-native-core`.
7
8#![deny(unsafe_op_in_unsafe_fn)]
9
10mod camera;
11mod custom_geometry;
12mod events;
13mod geojson;
14mod geometry;
15mod handle;
16mod json;
17mod logging;
18mod map;
19mod options;
20mod projection;
21mod render;
22mod resource;
23mod runtime;
24mod values;
25
26use crate::values::NativeValue;
27use maplibre_native_core as maplibre_core;
28use maplibre_native_sys as sys;
29
30pub use camera::{
31    AnimationOptions, BoundOptions, CameraFitOptions, CameraOptions, FreeCameraOptions,
32    ProjectionMode,
33};
34pub use custom_geometry::{CanonicalTileId, CustomGeometrySourceOptions};
35pub use events::{
36    MapId, OfflineOperationCompletedEvent, OfflineRegionResponseErrorEvent, OfflineRegionStatus,
37    OfflineRegionStatusEvent, OfflineRegionTileCountLimitEvent, RenderFrameEvent, RenderMapEvent,
38    RenderingStats, RuntimeEvent, RuntimeEventPayload, RuntimeEventSource, StyleImageMissingEvent,
39    TileActionEvent, TileId, UnknownRuntimeEventPayload,
40};
41pub use geojson::{Feature, FeatureIdentifier, GeoJson};
42pub use geometry::Geometry;
43pub use json::{JsonMember, JsonValue};
44pub use logging::{LogRecord, clear_log_callback, set_async_log_severity_mask, set_log_callback};
45pub use map::{
46    LocationIndicatorImageKind, MapHandle, RasterDemEncoding, SourceInfo, SourceType, StyleImage,
47    StyleImageInfo, StyleImageOptions, TileScheme, TileSourceOptions, VectorTileEncoding,
48};
49pub use maplibre_core::{
50    AmbientCacheOperation, ConstrainMode, Error, ErrorKind, LogEvent, LogSeverity, LogSeverityMask,
51    MapDebugOptions, MapMode, MapOptions, MapTileOptions, MapViewportOptions, NetworkStatus,
52    NorthOrientation, OfflineOperationKind, OfflineOperationResultKind, OfflineRegionDownloadState,
53    OpenGLContextProviderMask, RenderBackendMask, RenderMode, ResourceErrorReason, ResourceKind,
54    ResourceLoadingMethod, ResourcePriority, ResourceResponseStatus, ResourceStoragePolicy,
55    ResourceUsage, Result, RuntimeEventType, TileLodMode, TileOperation, ViewportMode,
56};
57pub use projection::MapProjectionHandle;
58pub use render::{
59    DetachedRenderSessionHandle, EglContextDescriptor, FeatureExtensionResult,
60    FeatureStateSelector, FrameNativePointer, FrameOpenGLTextureName,
61    MetalBorrowedTextureDescriptor, MetalContextDescriptor, MetalOwnedTextureDescriptor,
62    MetalOwnedTextureFrame, MetalOwnedTextureFrameHandle, MetalSurfaceDescriptor, NativePointer,
63    OpenGLBorrowedTextureDescriptor, OpenGLContextDescriptor, OpenGLOwnedTextureDescriptor,
64    OpenGLOwnedTextureFrame, OpenGLOwnedTextureFrameHandle, OpenGLSurfaceDescriptor,
65    PremultipliedRgba8Image, QueriedFeature, RenderSessionHandle, RenderTargetExtent,
66    RenderedFeatureQueryOptions, RenderedQueryGeometry, SourceFeatureQueryOptions,
67    TextureImageInfo, VulkanBorrowedTextureDescriptor, VulkanContextDescriptor,
68    VulkanOwnedTextureDescriptor, VulkanOwnedTextureFrame, VulkanOwnedTextureFrameHandle,
69    VulkanSurfaceDescriptor, WglContextDescriptor,
70};
71pub use resource::{
72    ByteRange, ResourceProviderDecision, ResourceRequest, ResourceRequestHandle, ResourceResponse,
73    ResourceTransformRequest,
74};
75pub use runtime::{
76    OfflineOperationHandle, OfflineRegionDefinition, OfflineRegionInfo, RuntimeHandle,
77    RuntimeOptions,
78};
79pub use values::{
80    EdgeInsets, LatLng, LatLngBounds, ProjectedMeters, Quaternion, ScreenBox, ScreenPoint,
81    UnitBezier, Vec3,
82};
83
84/// Error returned by consuming one-shot handle operations when the handle
85/// remains live and the operation can be retried.
86#[derive(Debug)]
87pub struct HandleOperationError<T> {
88    error: Error,
89    handle: T,
90}
91
92impl<T> HandleOperationError<T> {
93    pub(crate) fn new(error: Error, handle: T) -> Self {
94        Self { error, handle }
95    }
96
97    /// Returns the operation error.
98    pub fn error(&self) -> &Error {
99        &self.error
100    }
101
102    /// Returns the stable category for the operation error.
103    pub fn kind(&self) -> ErrorKind {
104        self.error.kind()
105    }
106
107    /// Returns the raw C status for native operation errors, when available.
108    pub fn raw_status(&self) -> Option<i32> {
109        self.error.raw_status()
110    }
111
112    /// Returns the copied diagnostic message for the operation error.
113    pub fn diagnostic(&self) -> &str {
114        self.error.diagnostic()
115    }
116
117    /// Returns the operation error, dropping the still-live handle.
118    pub fn into_error(self) -> Error {
119        self.error
120    }
121
122    /// Returns the still-live handle so the operation can be retried.
123    pub fn into_handle(self) -> T {
124        self.handle
125    }
126
127    /// Splits this error into the operation error and still-live handle.
128    pub fn into_parts(self) -> (Error, T) {
129        (self.error, self.handle)
130    }
131}
132
133impl<T> std::fmt::Display for HandleOperationError<T> {
134    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
135        self.error.fmt(f)
136    }
137}
138
139impl<T: std::fmt::Debug> std::error::Error for HandleOperationError<T> {}
140
141/// Error returned by offline operation result transfers.
142#[derive(Debug)]
143pub enum OfflineOperationTakeError<T> {
144    /// The native transfer failed before consuming the operation result.
145    Retryable(HandleOperationError<T>),
146    /// The native result was consumed, but copying it into Rust-owned data failed.
147    Consumed(Error),
148}
149
150impl<T> OfflineOperationTakeError<T> {
151    pub(crate) fn retryable(error: Error, handle: T) -> Self {
152        Self::Retryable(HandleOperationError::new(error, handle))
153    }
154
155    pub(crate) fn consumed(error: Error) -> Self {
156        Self::Consumed(error)
157    }
158
159    /// Returns the operation error.
160    pub fn error(&self) -> &Error {
161        match self {
162            Self::Retryable(error) => error.error(),
163            Self::Consumed(error) => error,
164        }
165    }
166
167    /// Returns the stable category for the operation error.
168    pub fn kind(&self) -> ErrorKind {
169        self.error().kind()
170    }
171
172    /// Returns the raw C status for native operation errors, when available.
173    pub fn raw_status(&self) -> Option<i32> {
174        self.error().raw_status()
175    }
176
177    /// Returns the copied diagnostic message for the operation error.
178    pub fn diagnostic(&self) -> &str {
179        self.error().diagnostic()
180    }
181
182    /// Returns the retryable error and still-live handle, if the operation was not consumed.
183    pub fn into_retryable(self) -> Option<HandleOperationError<T>> {
184        match self {
185            Self::Retryable(error) => Some(error),
186            Self::Consumed(_) => None,
187        }
188    }
189
190    /// Returns the operation error, dropping any retryable handle.
191    pub fn into_error(self) -> Error {
192        match self {
193            Self::Retryable(error) => error.into_error(),
194            Self::Consumed(error) => error,
195        }
196    }
197}
198
199impl<T> std::fmt::Display for OfflineOperationTakeError<T> {
200    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
201        self.error().fmt(f)
202    }
203}
204
205impl<T: std::fmt::Debug> std::error::Error for OfflineOperationTakeError<T> {}
206
207/// Returns the native C ABI contract version.
208pub fn c_version() -> u32 {
209    // SAFETY: mln_c_version takes no arguments and returns the process-global C
210    // ABI version for the linked native library.
211    unsafe { sys::mln_c_version() }
212}
213
214/// Returns the render backends compiled into the linked native library.
215pub fn supported_render_backends() -> RenderBackendMask {
216    // SAFETY: mln_supported_render_backend_mask takes no arguments and returns a
217    // value mask. Unknown future bits are preserved by from_bits_retain.
218    let mask = unsafe { sys::mln_supported_render_backend_mask() };
219    RenderBackendMask::from_bits_retain(mask)
220}
221
222/// Returns the OpenGL context providers compiled into the linked native library.
223pub fn supported_opengl_context_providers() -> OpenGLContextProviderMask {
224    // SAFETY: mln_opengl_supported_context_provider_mask takes no arguments and
225    // returns a value mask. Unknown future bits are preserved by from_bits_retain.
226    let mask = unsafe { sys::mln_opengl_supported_context_provider_mask() };
227    OpenGLContextProviderMask::from_bits_retain(mask)
228}
229
230/// Converts a geographic coordinate to Spherical Mercator projected meters.
231pub fn projected_meters_for_lat_lng(coordinate: LatLng) -> Result<ProjectedMeters> {
232    let mut raw_meters = sys::mln_projected_meters {
233        northing: 0.0,
234        easting: 0.0,
235    };
236    // SAFETY: coordinate is passed by value. out_meters points to valid
237    // writable storage for one projected-meter value.
238    maplibre_core::check(unsafe {
239        sys::mln_projected_meters_for_lat_lng(coordinate.to_native(), &mut raw_meters)
240    })?;
241    Ok(ProjectedMeters::from_native(raw_meters))
242}
243
244/// Converts Spherical Mercator projected meters to a geographic coordinate.
245pub fn lat_lng_for_projected_meters(meters: ProjectedMeters) -> Result<LatLng> {
246    let mut raw_coordinate = sys::mln_lat_lng {
247        latitude: 0.0,
248        longitude: 0.0,
249    };
250    // SAFETY: meters is passed by value. out_coordinate points to valid
251    // writable storage for one coordinate value.
252    maplibre_core::check(unsafe {
253        sys::mln_lat_lng_for_projected_meters(meters.to_native(), &mut raw_coordinate)
254    })?;
255    Ok(LatLng::from_native(raw_coordinate))
256}
257
258/// Reads MapLibre Native's process-global network status.
259pub fn network_status() -> Result<NetworkStatus> {
260    maplibre_core::network_status()
261}
262
263/// Sets MapLibre Native's process-global network status.
264pub fn set_network_status(status: NetworkStatus) -> Result<()> {
265    maplibre_core::set_network_status(status)
266}
267
268#[cfg(test)]
269fn set_network_status_raw(raw_status: u32) -> Result<()> {
270    maplibre_core::set_network_status_raw(raw_status)
271}
272
273#[cfg(test)]
274mod tests {
275    use static_assertions::assert_not_impl_any;
276
277    use super::*;
278
279    assert_not_impl_any!(RuntimeHandle: Send, Sync);
280    assert_not_impl_any!(MapHandle: Send, Sync);
281    assert_not_impl_any!(MapProjectionHandle: Send, Sync);
282    assert_not_impl_any!(NativePointer: Send, Sync);
283    assert_not_impl_any!(FrameNativePointer<'static>: Send, Sync);
284    assert_not_impl_any!(RenderSessionHandle: Send, Sync);
285
286    #[test]
287    // Spec coverage: BND-103.
288    fn projected_meter_helpers_round_trip() {
289        let coordinate = LatLng::new(45.0, -122.0);
290        let meters = projected_meters_for_lat_lng(coordinate).unwrap();
291        let round_tripped = lat_lng_for_projected_meters(meters).unwrap();
292
293        assert!((round_tripped.latitude - coordinate.latitude).abs() < 1e-9);
294        assert!((round_tripped.longitude - coordinate.longitude).abs() < 1e-9);
295    }
296
297    #[test]
298    // Spec coverage: BND-020.
299    fn invalid_network_status_reports_public_error() {
300        let error = set_network_status_raw(999_999).unwrap_err();
301
302        assert_eq!(error.kind(), ErrorKind::InvalidArgument);
303        assert_eq!(error.raw_status(), Some(sys::MLN_STATUS_INVALID_ARGUMENT));
304        assert!(error.diagnostic().contains("network status"));
305    }
306
307    #[test]
308    // Spec coverage: BND-025 and BND-068.
309    fn unknown_network_status_is_rejected_before_calling_c() {
310        let error = set_network_status(NetworkStatus::Unknown(999_999)).unwrap_err();
311
312        assert_eq!(error.kind(), ErrorKind::InvalidArgument);
313        assert_eq!(error.raw_status(), None);
314        assert!(error.diagnostic().contains("cannot be set"));
315    }
316}