diff --git a/platform/rust/Cargo.toml b/platform/rust/Cargo.toml index d5b7d327b43..b5b8fdb4000 100644 --- a/platform/rust/Cargo.toml +++ b/platform/rust/Cargo.toml @@ -7,6 +7,9 @@ edition = "2021" cxx = "1.0" clap = { version = "4.5.27", features = ["derive", "env"] } +[dev-dependencies] +insta = { version = "1.42.1" } + [build-dependencies] cmake = "0.1" cxx-build = "1.0" diff --git a/platform/rust/include/wrapper.h b/platform/rust/include/wrapper.h index 86224cdc58e..338d181cec8 100644 --- a/platform/rust/include/wrapper.h +++ b/platform/rust/include/wrapper.h @@ -1,11 +1,28 @@ #pragma once #include "rust/cxx.h" #include "mbgl/math/log2.hpp" +#include "mbgl/util/tile_server_options.hpp" namespace ml { namespace rust { uint32_t get_42(); +using namespace mbgl; + +// TileServerOptions constructor helpers +std::unique_ptr TileServerOptions_new(); +std::unique_ptr TileServerOptions_default(); +std::unique_ptr TileServerOptions_mapbox(); +std::unique_ptr TileServerOptions_maplibre(); +std::unique_ptr TileServerOptions_maptiler(); + +// TileServerOptions Optional helpers (not supported yet by cxx) +const int8_t * TileServerOptions_sourceVersionPrefix(const TileServerOptions& self); +const int8_t * TileServerOptions_styleVersionPrefix(const TileServerOptions& self); +const int8_t * TileServerOptions_spritesVersionPrefix(const TileServerOptions& self); +const int8_t * TileServerOptions_glyphsVersionPrefix(const TileServerOptions& self); +const int8_t * TileServerOptions_tileVersionPrefix(const TileServerOptions& self); + } // namespace rust } // namespace ml diff --git a/platform/rust/justfile b/platform/rust/justfile index f9e5604d1eb..73f9da6514b 100644 --- a/platform/rust/justfile +++ b/platform/rust/justfile @@ -44,6 +44,10 @@ fmt: test: cargo test --all-targets --workspace +# Run all tests and accept the changes. Requires cargo-insta to be installed. +test-accept: + cargo insta test --accept + # Test documentation test-doc: RUSTDOCFLAGS="-D warnings" cargo doc --no-deps diff --git a/platform/rust/src/lib.rs b/platform/rust/src/lib.rs index ab3e4fa8958..56d7badae2f 100644 --- a/platform/rust/src/lib.rs +++ b/platform/rust/src/lib.rs @@ -1,4 +1,10 @@ -#[cxx::bridge] +use std::ffi::{c_char, CStr}; +use std::fmt; +use std::fmt::Debug; + +use cxx::UniquePtr; + +#[cxx::bridge(namespace = "ml::rust")] mod ffi { // C++ types exposed to Rust. unsafe extern "C++" { @@ -14,18 +20,347 @@ mod ffi { pub fn ceil_log2(x: u64) -> u32; // A function defined in the C++ rust wrapper rather than the core lib. - #[namespace = "ml::rust"] pub fn get_42() -> u32; + + #[namespace = "mbgl"] + type TileServerOptions; + + /// Create a new default configuration + fn TileServerOptions_new() -> UniquePtr; + /// Get the tile server options configured for MapLibre. + fn TileServerOptions_mapbox() -> UniquePtr; + /// Get the tile server options configured for Mapbox. + fn TileServerOptions_maplibre() -> UniquePtr; + /// Get the tile server options configured for MapTiler. + fn TileServerOptions_maptiler() -> UniquePtr; + + // /// Sets the API base URL. + // #[cxx_name = "withBaseURL"] + // TileServerOptions& withBaseURL(std::string baseURL); + + /// Gets the base URL. + #[cxx_name = "baseURL"] + fn base_url(self: &TileServerOptions) -> &CxxString; + + // /// Sets the scheme alias for the tile server. For example maptiler:// for MapTiler. + // #[cxx_name = "withUriSchemeAlias"] + // TileServerOptions& withUriSchemeAlias(std::string alias); + + /// Gets the URI scheme alias. + #[cxx_name = "uriSchemeAlias"] + fn uri_scheme_alias(self: &TileServerOptions) -> &CxxString; + + // /// Sets the template for sources. + // #[cxx_name = "withSourceTemplate"] + // TileServerOptions& withSourceTemplate(std::string sourceTemplate, std::string domainName, std::optional versionPrefix); + + /// Gets the source template. + #[cxx_name = "sourceTemplate"] + fn source_template(self: &TileServerOptions) -> &CxxString; + + /// Gets the source domain name. + #[cxx_name = "sourceDomainName"] + fn source_domain_name(self: &TileServerOptions) -> &CxxString; + + /// Gets the source version prefix. + unsafe fn TileServerOptions_sourceVersionPrefix(value: &TileServerOptions) -> *const i8; + + // /// Sets the template for styles. If `domainName` is set, the URL domain must contain the specified + // /// string to be matched as canonical style URL. + // #[cxx_name = "withStyleTemplate"] + // TileServerOptions& withStyleTemplate(std::string styleTemplate, std::string domainName, std::optional versionPrefix); + + /// Gets the style template. + #[cxx_name = "styleTemplate"] + fn style_template(self: &TileServerOptions) -> &CxxString; + + /// Gets the style domain name. + #[cxx_name = "styleDomainName"] + fn style_domain_name(self: &TileServerOptions) -> &CxxString; + + /// Gets the style version prefix. + unsafe fn TileServerOptions_styleVersionPrefix(value: &TileServerOptions) -> *const i8; + // fn style_version_prefix(self: &TileServerOptions) -> &CxxString; + + // /// Sets the template for sprites. + // /// If `domainName` is set, the URL domain must contain the specified + // /// string to be matched as canonical sprite URL. + // #[cxx_name = "withSpritesTemplate"] + // TileServerOptions& withSpritesTemplate(std::string spritesTemplate, std::string domainName, std::optional versionPrefix); + + /// Gets the sprites template. + #[cxx_name = "spritesTemplate"] + fn sprites_template(self: &TileServerOptions) -> &CxxString; + + /// Gets the sprites domain name. + #[cxx_name = "spritesDomainName"] + fn sprites_domain_name(self: &TileServerOptions) -> &CxxString; + + /// Gets the sprites version prefix. + unsafe fn TileServerOptions_spritesVersionPrefix(value: &TileServerOptions) -> *const i8; + // fn sprites_version_prefix(self: &TileServerOptions) -> *const CxxString; + + // /// Sets the template for glyphs. + // /// If set, the URL domain must contain the specified + // /// string to be matched as canonical glyphs URL. + // #[cxx_name = "withGlyphsTemplate"] + // TileServerOptions& withGlyphsTemplate(std::string glyphsTemplate, std::string domainName, std::optional versionPrefix); + + /// Gets the glyphs template. + #[cxx_name = "glyphsTemplate"] + fn glyphs_template(self: &TileServerOptions) -> &CxxString; + + /// Gets the glyphs domain name. + #[cxx_name = "glyphsDomainName"] + fn glyphs_domain_name(self: &TileServerOptions) -> &CxxString; + + /// Gets the glyphs version prefix. + unsafe fn TileServerOptions_glyphsVersionPrefix(value: &TileServerOptions) -> *const i8; + // fn glyphs_version_prefix(self: &TileServerOptions) -> &CxxString; + + // /// Sets the template for tiles. + // /// + // /// If `domainName` is set, the URL domain must contain the specified + // /// string to be matched as canonical tile URL. + // #[cxx_name = "withTileTemplate"] + // TileServerOptions& withTileTemplate(std::string tileTemplate, std::string domainName, std::optional versionPrefix); + + /// Gets the tile template. + #[cxx_name = "tileTemplate"] + fn tile_template(self: &TileServerOptions) -> &CxxString; + + /// Gets the tile domain name. + #[cxx_name = "tileDomainName"] + fn tile_domain_name(self: &TileServerOptions) -> &CxxString; + + /// Gets the tile version prefix. + unsafe fn TileServerOptions_tileVersionPrefix(value: &TileServerOptions) -> *const i8; + // fn tile_version_prefix(self: &TileServerOptions) -> &CxxString; + + // /// Sets the access token parameter name. + // #[cxx_name = "withApiKeyParameterName"] + // TileServerOptions& withApiKeyParameterName(std::string apiKeyParameterName); + + /// Gets the API key parameter name. + #[cxx_name = "apiKeyParameterName"] + fn api_key_parameter_name(self: &TileServerOptions) -> &CxxString; + + // #[cxx_name = "setRequiresApiKey"] + // TileServerOptions& setRequiresApiKey(bool apiKeyRequired); + + /// Checks if an API key is required. + #[cxx_name = "requiresApiKey"] + fn requires_api_key(self: &TileServerOptions) -> bool; + + // /// Gets the default styles. + // #[cxx_name = "defaultStyles"] + // const std::vector defaultStyles() const; + + // /// Sets the collection default styles. + // #[cxx_name = "withDefaultStyles"] + // TileServerOptions& withDefaultStyles(std::vector styles); + + // #[cxx_name = "withDefaultStyle"] + // fn set_default_style(self: &TileServerOptions); + + /// Gets the default style. + #[cxx_name = "defaultStyle"] + fn default_style(self: &TileServerOptions) -> &CxxString; } } // Re-export native functions that do not need safety wrappers. -pub use ffi::{ceil_log2, get_42}; +pub use ffi::{ceil_log2, get_42, TileServerOptions}; + +unsafe fn to_opt<'a>(value: *const c_char) -> Option<&'a CStr> { + value.as_ref().map(|v| CStr::from_ptr(v)) +} + +// Add more methods to make usage more ergonomic +impl TileServerOptions { + /// Create a new default configuration + pub fn new() -> UniquePtr { + crate::ffi::TileServerOptions_new() + } + + /// Get the tile server options configured for MapLibre. + pub fn default_mapbox() -> UniquePtr { + crate::ffi::TileServerOptions_mapbox() + } + + /// Get the tile server options configured for Mapbox. + pub fn default_maplibre() -> UniquePtr { + crate::ffi::TileServerOptions_maplibre() + } + + /// Get the tile server options configured for MapTiler. + pub fn default_maptiler() -> UniquePtr { + crate::ffi::TileServerOptions_maptiler() + } + + /// Gets the source version prefix. + pub fn source_version_prefix(&self) -> Option<&CStr> { + unsafe { to_opt(crate::ffi::TileServerOptions_sourceVersionPrefix(self)) } + } + + /// Gets the style version prefix. + pub fn style_version_prefix(&self) -> Option<&CStr> { + unsafe { to_opt(crate::ffi::TileServerOptions_styleVersionPrefix(self)) } + } + + /// Gets the sprites version prefix. + pub fn sprites_version_prefix(&self) -> Option<&CStr> { + unsafe { to_opt(crate::ffi::TileServerOptions_spritesVersionPrefix(self)) } + } + + /// Gets the glyphs version prefix. + pub fn glyphs_version_prefix(&self) -> Option<&CStr> { + unsafe { to_opt(crate::ffi::TileServerOptions_glyphsVersionPrefix(self)) } + } + + /// Gets the tile version prefix. + pub fn tile_version_prefix(&self) -> Option<&CStr> { + unsafe { to_opt(crate::ffi::TileServerOptions_tileVersionPrefix(self)) } + } +} + +impl Debug for TileServerOptions { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("TileServerOptions") + .field("base_url", &self.base_url()) + .field("uri_scheme_alias", &self.uri_scheme_alias()) + .field("source_template", &self.source_template()) + .field("source_domain_name", &self.source_domain_name()) + .field("source_version_prefix", &self.source_version_prefix()) + .field("style_template", &self.style_template()) + .field("style_domain_name", &self.style_domain_name()) + .field("style_version_prefix", &self.style_version_prefix()) + .field("sprites_template", &self.sprites_template()) + .field("sprites_domain_name", &self.sprites_domain_name()) + .field("sprites_version_prefix", &self.sprites_version_prefix()) + .field("glyphs_template", &self.glyphs_template()) + .field("glyphs_domain_name", &self.glyphs_domain_name()) + .field("glyphs_version_prefix", &self.glyphs_version_prefix()) + .field("tile_template", &self.tile_template()) + .field("tile_domain_name", &self.tile_domain_name()) + .field("tile_version_prefix", &self.tile_version_prefix()) + .field("api_key_parameter_name", &self.api_key_parameter_name()) + .field("requires_api_key", &self.requires_api_key()) + .field("default_style", &self.default_style()) + .finish() + } +} #[cfg(test)] mod tests { + use insta::assert_debug_snapshot; + use super::*; + #[test] + fn test_defaults() { + assert_debug_snapshot!(TileServerOptions::new(), @r#" + TileServerOptions { + base_url: "", + uri_scheme_alias: "", + source_template: "", + source_domain_name: "", + source_version_prefix: None, + style_template: "", + style_domain_name: "", + style_version_prefix: None, + sprites_template: "", + sprites_domain_name: "", + sprites_version_prefix: None, + glyphs_template: "", + glyphs_domain_name: "", + glyphs_version_prefix: None, + tile_template: "", + tile_domain_name: "", + tile_version_prefix: None, + api_key_parameter_name: "", + requires_api_key: false, + default_style: "", + } + "#); + assert_debug_snapshot!(TileServerOptions::default_mapbox(), @r#" + TileServerOptions { + base_url: "https://demotiles.maplibre.org", + uri_scheme_alias: "maplibre", + source_template: "/tiles/{domain}.json", + source_domain_name: "", + source_version_prefix: None, + style_template: "{path}.json", + style_domain_name: "maps", + style_version_prefix: None, + sprites_template: "/{path}/sprite{scale}.{format}", + sprites_domain_name: "", + sprites_version_prefix: None, + glyphs_template: "/font/{fontstack}/{start}-{end}.pbf", + glyphs_domain_name: "fonts", + glyphs_version_prefix: None, + tile_template: "/{path}", + tile_domain_name: "tiles", + tile_version_prefix: None, + api_key_parameter_name: "", + requires_api_key: false, + default_style: "Basic", + } + "#); + assert_debug_snapshot!(TileServerOptions::default_maplibre(), @r#" + TileServerOptions { + base_url: "https://api.mapbox.com", + uri_scheme_alias: "mapbox", + source_template: "/{domain}.json", + source_domain_name: "", + source_version_prefix: Some( + "0\xe9\x9f", + ), + style_template: "/styles/v1{path}", + style_domain_name: "styles", + style_version_prefix: None, + sprites_template: "/styles/v1{directory}{filename}/sprite{extension}", + sprites_domain_name: "sprites", + sprites_version_prefix: None, + glyphs_template: "/fonts/v1{path}", + glyphs_domain_name: "fonts", + glyphs_version_prefix: None, + tile_template: "{path}", + tile_domain_name: "tiles", + tile_version_prefix: Some( + "0\xe9\x9f", + ), + api_key_parameter_name: "access_token", + requires_api_key: true, + default_style: "Streets", + } + "#); + assert_debug_snapshot!(TileServerOptions::default_maptiler(), @r#" + TileServerOptions { + base_url: "https://api.maptiler.com", + uri_scheme_alias: "maptiler", + source_template: "/tiles{path}/tiles.json", + source_domain_name: "sources", + source_version_prefix: None, + style_template: "/maps{path}/style.json", + style_domain_name: "maps", + style_version_prefix: None, + sprites_template: "/maps{path}", + sprites_domain_name: "sprites", + sprites_version_prefix: None, + glyphs_template: "/fonts{path}", + glyphs_domain_name: "fonts", + glyphs_version_prefix: None, + tile_template: "{path}", + tile_domain_name: "tiles", + tile_version_prefix: None, + api_key_parameter_name: "key", + requires_api_key: true, + default_style: "Streets", + } + "#); + } + #[test] fn test_rust_wrapper() { let result = get_42(); diff --git a/platform/rust/src/wrapper.cc b/platform/rust/src/wrapper.cc index c70acf7c360..b6426a028de 100644 --- a/platform/rust/src/wrapper.cc +++ b/platform/rust/src/wrapper.cc @@ -8,5 +8,41 @@ uint32_t get_42() { return 42; } +std::unique_ptr TileServerOptions_new() { + return std::make_unique(); +} +std::unique_ptr TileServerOptions_mapbox() { + return std::make_unique(TileServerOptions::MapLibreConfiguration()); +} +std::unique_ptr TileServerOptions_maplibre() { + return std::make_unique(TileServerOptions::MapboxConfiguration()); +} +std::unique_ptr TileServerOptions_maptiler() { + return std::make_unique(TileServerOptions::MapTilerConfiguration()); +} + +// FIXME: there should be a helper function to convert std::optional to const int8_t * + +const int8_t * TileServerOptions_sourceVersionPrefix(const TileServerOptions& self) { + auto v = self.sourceVersionPrefix(); + return (const int8_t *)(v.has_value() ? v->c_str() : nullptr); +} +const int8_t * TileServerOptions_styleVersionPrefix(const TileServerOptions& self) { + auto v = self.styleVersionPrefix(); + return (const int8_t *)(v.has_value() ? v->c_str() : nullptr); +} +const int8_t * TileServerOptions_spritesVersionPrefix(const TileServerOptions& self) { + auto v = self.spritesVersionPrefix(); + return (const int8_t *)(v.has_value() ? v->c_str() : nullptr); +} +const int8_t * TileServerOptions_glyphsVersionPrefix(const TileServerOptions& self) { + auto v = self.glyphsVersionPrefix(); + return (const int8_t *)(v.has_value() ? v->c_str() : nullptr); +} +const int8_t * TileServerOptions_tileVersionPrefix(const TileServerOptions& self) { + auto v = self.tileVersionPrefix(); + return (const int8_t *)(v.has_value() ? v->c_str() : nullptr); +} + } // namespace rust } // namespace ml