├── rustfmt.toml ├── .gitignore ├── src ├── vector_tile.rs ├── layer.rs ├── feature.rs ├── error.rs └── lib.rs ├── rust-toolchain ├── .gitmodules ├── .github ├── actions │ ├── post-build-env │ │ └── action.yml │ └── prepare-build-env │ │ └── action.yml ├── dependabot.yml └── workflows │ ├── publish.yml │ └── ci.yml ├── LICENSE ├── tests ├── common.rs ├── fixtures.rs ├── real_world.rs └── fixtures.test.js ├── Cargo.toml ├── README.md ├── package.json └── Cargo.lock /rustfmt.toml: -------------------------------------------------------------------------------- 1 | tab_spaces = 2 -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /.vscode 2 | /target 3 | /pkg 4 | /node_modules 5 | **/*.rs.bk -------------------------------------------------------------------------------- /src/vector_tile.rs: -------------------------------------------------------------------------------- 1 | #![allow(clippy::derive_partial_eq_without_eq)] 2 | include!(concat!(env!("OUT_DIR"), "/vector_tile.rs")); 3 | -------------------------------------------------------------------------------- /rust-toolchain: -------------------------------------------------------------------------------- 1 | [toolchain] 2 | channel = "1.91.1" 3 | components = ["clippy", "rustfmt", "rust-src"] 4 | targets = ["wasm32-unknown-unknown"] 5 | profile = "minimal" 6 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "vector-tile-spec"] 2 | path = vector-tile-spec 3 | url = https://github.com/mapbox/vector-tile-spec 4 | [submodule "mvt-fixtures"] 5 | path = mvt-fixtures 6 | url = https://github.com/mapbox/mvt-fixtures 7 | -------------------------------------------------------------------------------- /src/layer.rs: -------------------------------------------------------------------------------- 1 | //! This module provide the `Layer` struct. 2 | //! 3 | //! The `Layer` struct represents a layer in a vector tile, containing metadata about the layer and its features. 4 | //! 5 | //! # Types 6 | //! 7 | //! The `layer` module defines the following types: 8 | //! 9 | //! - `Layer`: Represents a layer in a vector tile, containing metadata about the layer and its features. 10 | 11 | /// A structure representing a layer in a vector tile. 12 | #[derive(Debug, Clone)] 13 | pub struct Layer { 14 | /// The index of the layer in the vector tile. 15 | pub layer_index: usize, 16 | 17 | /// The version of the layer. 18 | pub version: u32, 19 | 20 | /// The name of the layer. 21 | pub name: String, 22 | 23 | /// The number of features in the layer. 24 | pub feature_count: usize, 25 | 26 | /// The extent of the layer, representing the size of the tile in pixels. Defaults to 4096. 27 | pub extent: u32, 28 | } 29 | -------------------------------------------------------------------------------- /.github/actions/post-build-env/action.yml: -------------------------------------------------------------------------------- 1 | name: Post build environment 2 | description: Clean up build environment actions 3 | 4 | inputs: 5 | cargo-cache-primary-key: 6 | description: Primary key of cargo cache restore action 7 | required: true 8 | cargo-cache-hit: 9 | description: A boolean value to indicate an exact match was found for the cargo cache restore action 10 | required: true 11 | 12 | runs: 13 | using: "composite" 14 | steps: 15 | - name: Cache cargo output (save) 16 | if: inputs.cargo-cache-hit != 'true' && (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/develop') 17 | uses: actions/cache/save@v4.3.0 18 | with: 19 | path: | 20 | ~/.cargo/bin/ 21 | ~/.cargo/.crates.toml 22 | ~/.cargo/.crates2.json 23 | ~/.cargo/.package-cache 24 | ~/.cargo/registry/ 25 | ~/.cargo/git/db/ 26 | target/ 27 | key: ${{ inputs.cargo-cache-primary-key }} 28 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2023-now Paul Lange 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /tests/common.rs: -------------------------------------------------------------------------------- 1 | use serde::Deserialize; 2 | use std::fs::read_dir; 3 | use std::path::PathBuf; 4 | use std::{io::Error, result::Result}; 5 | 6 | type Fixture = (PathBuf, PathBuf, PathBuf); 7 | 8 | pub fn get_all_fixtures() -> Result, Error> { 9 | let mut result = Vec::new(); 10 | 11 | let mut entries: Vec<_> = read_dir("mvt-fixtures/fixtures")? 12 | .map(|entry| entry.unwrap()) 13 | .collect(); 14 | 15 | entries.sort_by(|a, b| { 16 | let name_a = a.file_name(); 17 | let name_b = b.file_name(); 18 | 19 | name_a.cmp(&name_b) 20 | }); 21 | 22 | for entry in entries { 23 | let mvt_file = entry.path().join("tile.mvt"); 24 | let tile_file = entry.path().join("tile.json"); 25 | let info_file = entry.path().join("info.json"); 26 | 27 | result.push((mvt_file, tile_file, info_file)); 28 | } 29 | Ok(result) 30 | } 31 | 32 | #[derive(Deserialize)] 33 | pub struct Validity { 34 | pub v1: bool, 35 | pub v2: bool, 36 | } 37 | 38 | #[derive(Deserialize)] 39 | pub struct TileInfo { 40 | pub description: String, 41 | pub validity: Validity, 42 | } 43 | 44 | #[derive(Deserialize)] 45 | pub struct Layer { 46 | pub name: Option, 47 | } 48 | 49 | #[derive(Deserialize)] 50 | pub struct TileContent { 51 | pub layers: Option>, 52 | } 53 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "mvt-reader" 3 | version = "2.2.0" 4 | description = "A library for decoding and reading mapbox vector tiles in Rust and WebAssembly" 5 | authors = ["Paul Lange "] 6 | repository = "https://github.com/codeart1st/mvt-reader" 7 | keywords = ["rust", "webassembly", "wasm", "pbf", "mvt"] 8 | include = ["/src", "/LICENSE", "/build.rs", "vector_tile.proto"] 9 | license = "MIT" 10 | edition = "2024" 11 | 12 | [lib] 13 | crate-type = ["cdylib", "rlib"] 14 | 15 | [features] 16 | wasm = ["wasm-bindgen", "serde-wasm-bindgen", "js-sys", "geojson", "serde", "serde_json"] 17 | 18 | [dependencies] 19 | geo-types = "0.7" 20 | prost = { version = "0.13", default-features = false, features = ["prost-derive", "std"] } 21 | wasm-bindgen = { version = "0.2", optional = true } 22 | serde-wasm-bindgen = { version = "0.6", optional = true } 23 | js-sys = { version = "0.3", optional = true } 24 | geojson = { version = "0.24", optional = true } 25 | serde = { version = "1.0", optional = true } 26 | serde_json = { version = "1.0", optional = true } 27 | 28 | [dev-dependencies] 29 | serde = { version = "1.0.228", features = ["derive"] } 30 | serde_json = "1.0.145" 31 | prost = { version = "0.13.5" } # for testing we need default-features to be enabled 32 | 33 | [build-dependencies] 34 | prost-build = "0.14.1" 35 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | 3 | updates: 4 | - package-ecosystem: cargo 5 | directory: / 6 | schedule: 7 | interval: daily 8 | target-branch: "develop" 9 | open-pull-requests-limit: 10 10 | groups: 11 | development-dependencies: 12 | dependency-type: "development" 13 | 14 | - package-ecosystem: npm 15 | directory: / 16 | schedule: 17 | interval: daily 18 | target-branch: "develop" 19 | open-pull-requests-limit: 10 20 | groups: 21 | development-dependencies: 22 | dependency-type: "development" 23 | 24 | - package-ecosystem: github-actions 25 | directory: / 26 | schedule: 27 | interval: daily 28 | target-branch: "develop" 29 | open-pull-requests-limit: 10 30 | groups: 31 | actions: 32 | dependency-type: "production" 33 | 34 | - package-ecosystem: github-actions 35 | directory: /.github/actions/prepare-build-env 36 | schedule: 37 | interval: daily 38 | target-branch: "develop" 39 | open-pull-requests-limit: 10 40 | groups: 41 | actions: 42 | dependency-type: "production" 43 | 44 | - package-ecosystem: github-actions 45 | directory: /.github/actions/post-build-env 46 | schedule: 47 | interval: daily 48 | target-branch: "develop" 49 | open-pull-requests-limit: 10 50 | groups: 51 | actions: 52 | dependency-type: "production" 53 | -------------------------------------------------------------------------------- /.github/workflows/publish.yml: -------------------------------------------------------------------------------- 1 | name: Publish 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | - beta 8 | - alpha 9 | 10 | permissions: 11 | contents: read 12 | 13 | jobs: 14 | ci: 15 | uses: ./.github/workflows/ci.yml 16 | 17 | publish: 18 | needs: ci 19 | timeout-minutes: 20 20 | runs-on: ubuntu-22.04 21 | permissions: 22 | contents: write 23 | issues: write 24 | pull-requests: write 25 | id-token: write 26 | steps: 27 | - name: Checkout repository and submodules 28 | uses: actions/checkout@v6.0.1 29 | with: 30 | submodules: recursive 31 | fetch-depth: 0 32 | 33 | - name: Prepare build environemnt 34 | uses: ./.github/actions/prepare-build-env 35 | 36 | - name: Download wasm-pack build output 37 | uses: actions/download-artifact@v6.0.0 38 | with: 39 | name: wasm-pack-build 40 | path: pkg 41 | 42 | - name: Setup node 43 | uses: actions/setup-node@v6.1.0 44 | with: 45 | node-version: "lts/*" 46 | 47 | - name: Install dependencies 48 | run: npm ci 49 | 50 | - name: Verify npm package signatures 51 | run: npm audit signatures 52 | 53 | - name: Run semantic-release 54 | env: 55 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 56 | CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }} 57 | run: npx semantic-release 58 | -------------------------------------------------------------------------------- /src/feature.rs: -------------------------------------------------------------------------------- 1 | //! This module provides types and utilities for working with features in the `mvt-reader` crate. 2 | //! 3 | //! A feature represents a geographic entity with geometry and associated properties. Features are typically found within layers of a vector tile. 4 | //! 5 | //! # Types 6 | //! 7 | //! The `feature` module defines the following types: 8 | //! 9 | //! - `Feature`: Represents a feature with geometry, an optional id and optional properties. 10 | 11 | use std::collections::HashMap; 12 | 13 | use geo_types::Geometry; 14 | 15 | /// An enumeration representing the value of a property associated with a feature. 16 | #[derive(Debug, Clone, PartialEq, PartialOrd)] 17 | pub enum Value { 18 | String(String), 19 | Float(f32), 20 | Double(f64), 21 | Int(i64), 22 | UInt(u64), 23 | SInt(i64), 24 | Bool(bool), 25 | Null, 26 | } 27 | 28 | /// A structure representing a feature in a vector tile. 29 | #[derive(Debug, Clone)] 30 | pub struct Feature { 31 | /// The geometry of the feature. 32 | pub geometry: Geometry, 33 | 34 | /// Optional identifier for the feature. 35 | pub id: Option, 36 | 37 | /// Optional properties associated with the feature. 38 | pub properties: Option>, 39 | } 40 | 41 | impl Feature { 42 | /// Retrieves the geometry of the feature. 43 | /// 44 | /// # Returns 45 | /// 46 | /// A reference to the geometry of the feature. 47 | /// 48 | /// # Examples 49 | /// 50 | /// ``` 51 | /// use mvt_reader::feature::Feature; 52 | /// use geo_types::{Geometry, Point}; 53 | /// 54 | /// let feature = Feature { 55 | /// geometry: Geometry::Point(Point::new(0.0, 0.0)), 56 | /// id: None, 57 | /// properties: None, 58 | /// }; 59 | /// 60 | /// let geometry = feature.get_geometry(); 61 | /// println!("{:?}", geometry); 62 | /// ``` 63 | pub fn get_geometry(&self) -> &Geometry { 64 | &self.geometry 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /.github/actions/prepare-build-env/action.yml: -------------------------------------------------------------------------------- 1 | name: Prepare build environment 2 | description: Ensures that everything is prepared for our build jobs 3 | 4 | outputs: 5 | cargo-cache-primary-key: 6 | description: Primary key of cargo cache restore action 7 | value: ${{ steps.cargo-cache.outputs.cache-primary-key }} 8 | cargo-cache-hit: 9 | description: A boolean value to indicate an exact match was found for the cargo cache restore action 10 | value: ${{ steps.cargo-cache.outputs.cache-hit }} 11 | 12 | runs: 13 | using: "composite" 14 | steps: 15 | - name: Use protobuf-compiler latest 16 | shell: bash 17 | run: sudo apt-get install -y protobuf-compiler 18 | 19 | - name: Use wasm-pack latest 20 | shell: bash 21 | run: curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh 22 | 23 | - name: Cache rust-toolchain (restore) 24 | id: rustup-cache 25 | uses: actions/cache/restore@v4.3.0 26 | with: 27 | path: | 28 | ~/.rustup/toolchains 29 | ~/.rustup/update-hashes 30 | ~/.rustup/settings.toml 31 | key: toolchain-${{ hashFiles('rust-toolchain') }}-1 32 | 33 | - name: Use rust-toolchain 34 | if: steps.rustup-cache.outputs.cache-hit != 'true' 35 | shell: bash 36 | run: | 37 | rustup toolchain uninstall stable 38 | rustup show # force install of rust-toolchain TOML 39 | 40 | - name: Get rust version 41 | id: rustup 42 | shell: bash 43 | run: | 44 | rustup show 45 | echo "version=$(rustc --version | cut -d ' ' -f 2)" >> $GITHUB_OUTPUT 46 | 47 | - name: Cache rust-toolchain (save) 48 | uses: actions/cache/save@v4.3.0 49 | if: steps.rustup-cache.outputs.cache-hit != 'true' && (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/develop') 50 | with: 51 | path: | 52 | ~/.rustup/toolchains 53 | ~/.rustup/update-hashes 54 | ~/.rustup/settings.toml 55 | key: ${{ steps.rustup-cache.outputs.cache-primary-key }} 56 | 57 | - name: Cache cargo output (restore) 58 | id: cargo-cache 59 | uses: actions/cache/restore@v4.3.0 60 | with: 61 | path: | 62 | ~/.cargo/bin/ 63 | ~/.cargo/.crates.toml 64 | ~/.cargo/.crates2.json 65 | ~/.cargo/.package-cache 66 | ~/.cargo/registry/ 67 | ~/.cargo/git/db/ 68 | target/ 69 | key: ${{ runner.os }}-${{ steps.rustup.outputs.version }}-cargo-${{ hashFiles('**/Cargo.lock') }}-${{ github.job }}-1 70 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # mvt-reader 2 | 3 |
4 | Mapbox vector tile reader in Rust 5 |
6 |
7 | A library for decoding and reading mapbox vector tiles in Rust and WebAssembly 8 |
9 |
10 | 18 | 19 | ## Features 20 | 21 | - Decodes and reads Mapbox vector tiles in Rust 22 | - Provides an API for accessing layer names and features within a vector tile 23 | - Can be used as a WebAssembly module in JavaScript (enabled by the `wasm` feature) 24 | 25 | ## Build the project 26 | 27 | ```sh 28 | cargo build --release 29 | ``` 30 | 31 | ## Run tests 32 | 33 | ```sh 34 | cargo test 35 | wasm-pack build --release --target nodejs -d pkg/node -- --features wasm && npm test 36 | ``` 37 | 38 | ## Usage 39 | 40 | To use the `mvt-reader` library in your Rust project, add the following to your `Cargo.toml` file: 41 | 42 | ```toml 43 | [dependencies] 44 | mvt-reader = "2.2.0" 45 | ``` 46 | 47 | Then, you can import and use the library in your code: 48 | 49 | ```rust 50 | use mvt_reader::{Reader, ParserError}; 51 | 52 | fn main() -> Result<(), ParserError> { 53 | // Read a vector tile from file or data 54 | let data = vec![/* Vector tile data */]; 55 | let reader = Reader::new(data)?; 56 | 57 | // Get layer names 58 | let layer_names = reader.get_layer_names()?; 59 | for name in layer_names { 60 | println!("Layer: {}", name); 61 | } 62 | 63 | // Get features for a specific layer 64 | let layer_index = 0; 65 | let features = reader.get_features(layer_index)?; 66 | for feature in features { 67 | todo!() 68 | } 69 | 70 | Ok(()) 71 | } 72 | ``` 73 | 74 | ## WebAssembly Usage 75 | To use the mvt-reader library as a WebAssembly module in JavaScript, you can install it with npm and use it in your JavaScript code: 76 | 77 | ```js 78 | const { Reader } = require('mvt-reader') 79 | const fs = require('fs') 80 | 81 | // Example usage 82 | const reader = new Reader(fs.readFileSync('path/to/tile.mvt')) 83 | const layerNames = reader.getLayerNames() 84 | console.log(layerNames) 85 | 86 | // More code... 87 | ``` 88 | 89 | ## License 90 | 91 | This project is licensed under the [MIT License](LICENSE). 92 | 93 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "scripts": { 4 | "test": "jest ./tests" 5 | }, 6 | "engines": { 7 | "node": ">=18" 8 | }, 9 | "devDependencies": { 10 | "semantic-release-replace-plugin": "1.2.7", 11 | "@semantic-release-cargo/semantic-release-cargo": "2.4.84", 12 | "@semantic-release/git": "10.0.1", 13 | "@semantic-release/github": "12.0.2", 14 | "jest": "30.2.0", 15 | "semantic-release": "25.0.2" 16 | }, 17 | "jest": { 18 | "modulePathIgnorePatterns": [ 19 | "/package.json", 20 | "/pkg/package.json" 21 | ] 22 | }, 23 | "release": { 24 | "branches": [ 25 | "main", 26 | { 27 | "name": "beta", 28 | "prerelease": true 29 | }, 30 | { 31 | "name": "alpha", 32 | "prerelease": true 33 | } 34 | ], 35 | "plugins": [ 36 | "@semantic-release/commit-analyzer", 37 | "@semantic-release/release-notes-generator", 38 | [ 39 | "semantic-release-replace-plugin", 40 | { 41 | "replacements": [ 42 | { 43 | "files": ["README.md", "pkg/README.md", "src/lib.rs"], 44 | "from": "(?<=mvt-reader = \\\")\\d+\\.\\d+\\.\\d+(-\\w+\\.\\d+)?(?=\\\")", 45 | "to": "${nextRelease.version}", 46 | "results": [ 47 | { 48 | "file": "README.md", 49 | "hasChanged": true, 50 | "numMatches": 1, 51 | "numReplacements": 1 52 | }, 53 | { 54 | "file": "pkg/README.md", 55 | "hasChanged": true, 56 | "numMatches": 1, 57 | "numReplacements": 1 58 | }, 59 | { 60 | "file": "src/lib.rs", 61 | "hasChanged": true, 62 | "numMatches": 1, 63 | "numReplacements": 1 64 | } 65 | ], 66 | "countMatches": true 67 | }, 68 | { 69 | "files": ["src/lib.rs"], 70 | "from": "(?<=version = \\\")\\d+\\.\\d+\\.\\d+(-\\w+\\.\\d+)?(?=\\\")", 71 | "to": "${nextRelease.version}", 72 | "results": [ 73 | { 74 | "file": "src/lib.rs", 75 | "hasChanged": true, 76 | "numMatches": 1, 77 | "numReplacements": 1 78 | } 79 | ], 80 | "countMatches": true 81 | } 82 | ] 83 | } 84 | ], 85 | "@semantic-release-cargo/semantic-release-cargo", 86 | [ 87 | "@semantic-release/npm", 88 | { 89 | "pkgRoot": "pkg" 90 | } 91 | ], 92 | "@semantic-release/github", 93 | [ 94 | "@semantic-release/git", 95 | { 96 | "assets": [ 97 | "Cargo.toml", 98 | "README.md", 99 | "src/lib.rs" 100 | ], 101 | "message": "chore(release): ${nextRelease.version} [skip ci]\n\n${nextRelease.notes}" 102 | } 103 | ] 104 | ] 105 | }, 106 | "publishConfig": { 107 | "provenance": true 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /tests/fixtures.rs: -------------------------------------------------------------------------------- 1 | mod common; 2 | 3 | use common::{get_all_fixtures, TileContent, TileInfo}; 4 | 5 | use std::fs::{read, read_to_string}; 6 | use std::{io::Error, result::Result}; 7 | 8 | use mvt_reader::Reader; 9 | 10 | #[test] 11 | fn read_all_fixtures() -> Result<(), Error> { 12 | for (mvt_file, tile_file, info_file) in get_all_fixtures()?.iter() { 13 | println!("Read {:?}", mvt_file); 14 | let bytes = read(mvt_file)?; 15 | let reader_result = Reader::new(bytes.to_vec()); 16 | match reader_result { 17 | Ok(reader) => { 18 | let tile_str = read_to_string(tile_file)?; 19 | let tile_json: TileContent = serde_json::from_str(tile_str.as_str())?; 20 | let layer_names_result = reader.get_layer_names(); 21 | 22 | let layer_names = match layer_names_result { 23 | Ok(names) => names, 24 | Err(error) => { 25 | println!("{:?}", error); 26 | let info_str = read_to_string(info_file)?; 27 | let info_json: TileInfo = serde_json::from_str(info_str.as_str())?; 28 | 29 | assert!(!info_json.validity.v1 && !info_json.validity.v2); 30 | println!("Failed correctly: {}", info_json.description); 31 | continue; 32 | } 33 | }; 34 | 35 | if let Some(layers) = tile_json.layers { 36 | for layer in layers { 37 | match layer.name { 38 | Some(layer_name) => { 39 | assert!(layer_names.contains(&layer_name)); 40 | } 41 | None => { 42 | let info_str = read_to_string(info_file)?; 43 | let info_json: TileInfo = serde_json::from_str(info_str.as_str())?; 44 | 45 | assert!(!info_json.validity.v1 && !info_json.validity.v2); 46 | println!( 47 | "Failed correctly, because missing layer name: {}", 48 | info_json.description 49 | ); 50 | } 51 | } 52 | } 53 | } 54 | 55 | for (i, _) in layer_names.iter().enumerate() { 56 | let features = reader.get_features(i); 57 | match features { 58 | Ok(features) => { 59 | println!("Parsed {} features", features.len()); 60 | } 61 | Err(error) => { 62 | let info_str = read_to_string(info_file)?; 63 | let info_json: TileInfo = serde_json::from_str(info_str.as_str())?; 64 | let mvt_file_path_string = mvt_file.to_str().unwrap(); 65 | 66 | println!("{:?}", error); 67 | assert!( 68 | (!info_json.validity.v1 && !info_json.validity.v2) 69 | || mvt_file_path_string.contains("016") // unknown geometry type 70 | || mvt_file_path_string.contains("039") // unknown geometry type 71 | ); 72 | println!( 73 | "Failed correctly, because incorrect features: {}", 74 | info_json.description 75 | ); 76 | } 77 | } 78 | } 79 | 80 | println!("{:?}", layer_names); 81 | } 82 | Err(_) => { 83 | let info_str = read_to_string(info_file)?; 84 | let info_json: TileInfo = serde_json::from_str(info_str.as_str())?; 85 | 86 | assert!(!info_json.validity.v1 && !info_json.validity.v2); 87 | println!("Failed asdf correctly: {}", info_json.description); 88 | } 89 | } 90 | } 91 | Ok(()) 92 | } 93 | -------------------------------------------------------------------------------- /tests/real_world.rs: -------------------------------------------------------------------------------- 1 | use std::error::Error; 2 | use std::fs::{DirEntry, read, read_dir}; 3 | use std::path::PathBuf; 4 | use std::{io, result::Result}; 5 | 6 | use mvt_reader::error::TagsError; 7 | use mvt_reader::{Reader, error::GeometryError}; 8 | 9 | #[test] 10 | fn read_corrupted_geometry_fixture() -> Result<(), io::Error> { 11 | let bytes = [ 12 | 0x1a, 0x19, 0x12, 0x14, 0x10, 0x3d, 0x20, 0x11, 0x20, 0x28, 0x20, 0x28, 0x20, 0x20, 0x20, 0x20, 13 | 0x20, 0x38, 0x20, 0x20, 0x18, 0x03, 0x40, 0x11, 0x60, 0xef, 0x23, 14 | ]; 15 | let reader_result = Reader::new(bytes.to_vec()); 16 | match reader_result { 17 | Ok(reader) => { 18 | let features = reader.get_features(0); 19 | match features { 20 | Ok(_) => { 21 | panic!("Parsing should have failed") 22 | } 23 | Err(error) => match error.source() { 24 | Some(error) if error.is::() => { 25 | println!("Expected error: {}", error); 26 | } 27 | _ => { 28 | panic!("Unexpected error: {}", error) 29 | } 30 | }, 31 | } 32 | } 33 | Err(_) => { 34 | panic!("Parsing failed unexpectedly") 35 | } 36 | } 37 | Ok(()) 38 | } 39 | 40 | #[test] 41 | fn read_corrupted_tags_fixture() -> Result<(), io::Error> { 42 | let bytes = [ 43 | 0x1a, 0x19, 0x12, 0x14, 0x10, 0x0, 0x10, 0x0, 0x20, 0x20, 0x10, 0x28, 0x20, 0x28, 0xe0, 0xdf, 44 | 0x20, 0x20, 0x20, 0x32, 0x20, 0x20, 0x18, 0x1, 0x60, 0xef, 0x1b, 45 | ]; 46 | let reader_result = Reader::new(bytes.to_vec()); 47 | match reader_result { 48 | Ok(reader) => { 49 | let features = reader.get_features(0); 50 | match features { 51 | Ok(_) => { 52 | panic!("Parsing should have failed") 53 | } 54 | Err(error) => match error.source() { 55 | Some(error) if error.is::() => { 56 | println!("Expected error: {}", error); 57 | } 58 | _ => { 59 | panic!("Unexpected error: {}", error) 60 | } 61 | }, 62 | } 63 | } 64 | Err(_) => { 65 | panic!("Parsing failed unexpectedly") 66 | } 67 | } 68 | Ok(()) 69 | } 70 | 71 | #[test] 72 | fn read_all_fixtures() -> Result<(), io::Error> { 73 | for mvt_file in get_all_real_world_fixtures()?.iter() { 74 | if !mvt_file.extension().unwrap().eq_ignore_ascii_case("mvt") { 75 | println!("Skipped file {:?}", mvt_file); 76 | continue; 77 | } 78 | println!("Read {:?}", mvt_file); 79 | let bytes = read(mvt_file)?; 80 | let reader_result = Reader::new(bytes.to_vec()); 81 | match reader_result { 82 | Ok(reader) => { 83 | let layer_names = match reader.get_layer_names() { 84 | Ok(layer_names) => layer_names, 85 | Err(error) => { 86 | panic!("{}", error); 87 | } 88 | }; 89 | for (i, _) in layer_names.iter().enumerate() { 90 | let features = reader.get_features(i); 91 | assert!(!features.unwrap().is_empty()); 92 | } 93 | println!("found layer names: {:?}", layer_names); 94 | } 95 | Err(_) => { 96 | panic!("Parsing failed unexpectedly") 97 | } 98 | } 99 | } 100 | Ok(()) 101 | } 102 | 103 | pub fn get_sorted_dir_entries(path: &str) -> std::io::Result> { 104 | let mut entries: Vec<_> = read_dir(path)?.map(|entry| entry.unwrap()).collect(); 105 | 106 | entries.sort_by(|a, b| { 107 | let name_a = a.file_name(); 108 | let name_b = b.file_name(); 109 | 110 | name_a.cmp(&name_b) 111 | }); 112 | 113 | Ok(entries) 114 | } 115 | 116 | pub fn get_all_real_world_fixtures() -> Result, io::Error> { 117 | let mut result = Vec::new(); 118 | 119 | let countries = get_sorted_dir_entries("mvt-fixtures/real-world")?; 120 | for country in countries { 121 | let fixtures = get_sorted_dir_entries(country.path().to_str().unwrap())?; 122 | for fixture in fixtures { 123 | result.push(fixture.path()); 124 | } 125 | } 126 | Ok(result) 127 | } 128 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | workflow_call: 5 | schedule: 6 | - cron: "0 7 * * 1" 7 | push: 8 | branches: 9 | - '*' 10 | - '!main' 11 | - '!beta' 12 | - '!alpha' 13 | 14 | env: 15 | CARGO_TERM_COLOR: always 16 | CARGO_INCREMENTAL: 0 17 | 18 | permissions: 19 | contents: read 20 | 21 | jobs: 22 | lint: 23 | timeout-minutes: 10 24 | runs-on: ubuntu-22.04 25 | steps: 26 | - name: Checkout repository and submodules 27 | uses: actions/checkout@v6.0.1 28 | with: 29 | submodules: recursive 30 | 31 | - name: Prepare build environemnt 32 | id: prepare 33 | uses: ./.github/actions/prepare-build-env 34 | 35 | - name: Run cargo clippy 36 | run: | 37 | cargo clippy --tests -- --deny "warnings" 38 | 39 | - name: Post build environemnt 40 | if: always() 41 | uses: ./.github/actions/post-build-env 42 | with: 43 | cargo-cache-primary-key: ${{ steps.prepare.outputs.cargo-cache-primary-key }} 44 | cargo-cache-hit: ${{ steps.prepare.outputs.cargo-cache-hit }} 45 | 46 | build_native: 47 | timeout-minutes: 10 48 | runs-on: ubuntu-22.04 49 | steps: 50 | - name: Checkout repository and submodules 51 | uses: actions/checkout@v6.0.1 52 | with: 53 | submodules: recursive 54 | 55 | - name: Prepare build environemnt 56 | id: prepare 57 | uses: ./.github/actions/prepare-build-env 58 | 59 | - name: Run cargo build 60 | run: cargo build 61 | 62 | - name: Post build environemnt 63 | if: always() 64 | uses: ./.github/actions/post-build-env 65 | with: 66 | cargo-cache-primary-key: ${{ steps.prepare.outputs.cargo-cache-primary-key }} 67 | cargo-cache-hit: ${{ steps.prepare.outputs.cargo-cache-hit }} 68 | 69 | build_wasm: 70 | timeout-minutes: 10 71 | runs-on: ubuntu-22.04 72 | steps: 73 | - name: Checkout repository and submodules 74 | uses: actions/checkout@v6.0.1 75 | with: 76 | submodules: recursive 77 | 78 | - name: Prepare build environemnt 79 | id: prepare 80 | uses: ./.github/actions/prepare-build-env 81 | 82 | - name: Run wasm-pack build 83 | run: | 84 | wasm-pack build --release --target web -- --features wasm 85 | wasm-pack build --release --target nodejs -d pkg/node -- --features wasm 86 | 87 | - name: Upload wasm-pack build output 88 | uses: actions/upload-artifact@v5.0.0 89 | with: 90 | name: wasm-pack-build 91 | path: pkg 92 | 93 | - name: Post build environemnt 94 | if: always() 95 | uses: ./.github/actions/post-build-env 96 | with: 97 | cargo-cache-primary-key: ${{ steps.prepare.outputs.cargo-cache-primary-key }} 98 | cargo-cache-hit: ${{ steps.prepare.outputs.cargo-cache-hit }} 99 | 100 | test_native: 101 | timeout-minutes: 10 102 | runs-on: ubuntu-22.04 103 | steps: 104 | - name: Checkout repository and submodules 105 | uses: actions/checkout@v6.0.1 106 | with: 107 | submodules: recursive 108 | 109 | - name: Prepare build environemnt 110 | id: prepare 111 | uses: ./.github/actions/prepare-build-env 112 | 113 | - name: Run cargo test 114 | run: | 115 | cargo test -- --nocapture 116 | 117 | - name: Post build environemnt 118 | if: always() 119 | uses: ./.github/actions/post-build-env 120 | with: 121 | cargo-cache-primary-key: ${{ steps.prepare.outputs.cargo-cache-primary-key }} 122 | cargo-cache-hit: ${{ steps.prepare.outputs.cargo-cache-hit }} 123 | 124 | test_wasm: 125 | needs: build_wasm 126 | timeout-minutes: 10 127 | runs-on: ubuntu-22.04 128 | steps: 129 | - name: Checkout repository and submodules 130 | uses: actions/checkout@v6.0.1 131 | with: 132 | submodules: recursive 133 | 134 | - name: Download wasm-pack build output 135 | uses: actions/download-artifact@v6.0.0 136 | with: 137 | name: wasm-pack-build 138 | path: pkg 139 | 140 | - name: Setup node 141 | uses: actions/setup-node@v6.1.0 142 | with: 143 | node-version: "lts/*" 144 | 145 | - name: Run jest test 146 | run: | 147 | npm ci 148 | npm test -------------------------------------------------------------------------------- /tests/fixtures.test.js: -------------------------------------------------------------------------------- 1 | const { Reader } = require('mvt-reader') 2 | const { readFileSync } = require('fs') 3 | 4 | describe('Test fixture', () => { 5 | test('032 contains layer metadata', async () => { 6 | const reader = new Reader(readFileSync('mvt-fixtures/fixtures/032/tile.mvt')) 7 | const layers = reader.getLayerMetadata() 8 | 9 | expect(layers.length).toBe(1) 10 | 11 | const layer = layers[0] 12 | 13 | expect(layer.name).toBe('hello') 14 | expect(layer.extent).toBe(4096) 15 | expect(layer.version).toBe(2) 16 | expect(layer.feature_count).toBe(1) 17 | expect(layer.layer_index).toBe(0) 18 | }) 19 | 20 | test('032 parsed successfully (string_value)', async () => { 21 | const reader = new Reader(readFileSync('mvt-fixtures/fixtures/032/tile.mvt')) 22 | expect(reader.getLayerNames()).toContain('hello') 23 | expect(reader.getFeatures(0)).toStrictEqual([ 24 | { 25 | geometry: { coordinates: [[25, 17]], type: 'MultiPoint' }, 26 | id: 1, 27 | properties: { key1: 'i am a string value' }, 28 | type: 'Feature' 29 | } 30 | ]) 31 | }) 32 | 33 | test('033 parsed successfully (float_value)', async () => { 34 | const reader = new Reader(readFileSync('mvt-fixtures/fixtures/033/tile.mvt')) 35 | expect(reader.getLayerNames()).toContain('hello') 36 | expect(reader.getFeatures(0)).toStrictEqual([ 37 | { 38 | geometry: { coordinates: [[25, 17]], type: 'MultiPoint' }, 39 | id: 1, 40 | properties: { key1: 3.0999999046325684 }, 41 | type: 'Feature' 42 | } 43 | ]) 44 | }) 45 | 46 | test('034 parsed successfully (double_value)', async () => { 47 | const reader = new Reader(readFileSync('mvt-fixtures/fixtures/034/tile.mvt')) 48 | expect(reader.getLayerNames()).toContain('hello') 49 | expect(reader.getFeatures(0)).toStrictEqual([ 50 | { 51 | geometry: { coordinates: [[25, 17]], type: 'MultiPoint' }, 52 | id: 1, 53 | properties: { key1: 1.23 }, 54 | type: 'Feature' 55 | } 56 | ]) 57 | }) 58 | 59 | test('035 parsed successfully (int_value)', async () => { 60 | const reader = new Reader(readFileSync('mvt-fixtures/fixtures/035/tile.mvt')) 61 | expect(reader.getLayerNames()).toContain('hello') 62 | expect(reader.getFeatures(0)).toStrictEqual([ 63 | { 64 | geometry: { coordinates: [[25, 17]], type: 'MultiPoint' }, 65 | id: 1, 66 | properties: { key1: 6 }, 67 | type: 'Feature' 68 | } 69 | ]) 70 | }) 71 | 72 | test('036 parsed successfully (uint_value)', async () => { 73 | const reader = new Reader(readFileSync('mvt-fixtures/fixtures/036/tile.mvt')) 74 | expect(reader.getLayerNames()).toContain('hello') 75 | expect(reader.getFeatures(0)).toStrictEqual([ 76 | { 77 | geometry: { coordinates: [[25, 17]], type: 'MultiPoint' }, 78 | id: 1, 79 | properties: { key1: 87948 }, 80 | type: 'Feature' 81 | } 82 | ]) 83 | }) 84 | 85 | test('037 parsed successfully (sint_value)', async () => { 86 | const reader = new Reader(readFileSync('mvt-fixtures/fixtures/037/tile.mvt')) 87 | expect(reader.getLayerNames()).toContain('hello') 88 | expect(reader.getFeatures(0)).toStrictEqual([ 89 | { 90 | geometry: { coordinates: [[25, 17]], type: 'MultiPoint' }, 91 | id: 1, 92 | properties: { key1: 87948 }, 93 | type: 'Feature' 94 | } 95 | ]) 96 | }) 97 | 98 | test('038 parsed successfully (different value types)', async () => { 99 | const reader = new Reader(readFileSync('mvt-fixtures/fixtures/038/tile.mvt')) 100 | expect(reader.getLayerNames()).toContain('hello') 101 | expect(reader.getFeatures(0)).toStrictEqual([ 102 | { 103 | geometry: { coordinates: [[25, 17]], type: 'MultiPoint' }, 104 | id: 1, 105 | properties: { 106 | string_value: 'ello', 107 | bool_value: true, 108 | int_value: 6, 109 | double_value: 1.23, 110 | float_value: 3.0999999046325684, 111 | sint_value: -87948, 112 | uint_value: 87948 113 | }, 114 | type: 'Feature' 115 | } 116 | ]) 117 | }) 118 | 119 | test('013 failed successfully', async () => { 120 | let error 121 | const reader = new Reader(readFileSync('mvt-fixtures/fixtures/013/tile.mvt'), e => { 122 | error = e 123 | }) 124 | expect(reader).toBeDefined() 125 | expect(error).toBe('ParserError { source: DecodeError { source: DecodeError { description: \"invalid wire type: Varint (expected LengthDelimited)\", stack: [(\"Layer\", \"keys\"), (\"Tile\", \"layers\")] } } }') 126 | 127 | expect(reader.getLayerNames()).toBeNull() 128 | expect(reader.getFeatures(0)).toBeNull() 129 | }) 130 | }) -------------------------------------------------------------------------------- /src/error.rs: -------------------------------------------------------------------------------- 1 | //! This module provides error types and utilities for the `mvt-reader` crate. 2 | //! 3 | //! # Errors 4 | //! 5 | //! The `error` module defines the following error types: 6 | //! 7 | //! - `ParserError`: Represents an error that occurs during parsing of a vector tile. 8 | //! - `GeometryError`: Represents an error related to the geometry of a feature in a vector tile. 9 | //! - `TagsError`: Represents an error related to the tags of a feature in a vector tile. 10 | //! - `VersionError`: Represents an error related to the version of a vector tile. 11 | //! - `DecodeError`: Represents an error indicating a decoding failure during the parsing of a vector tile. 12 | //! 13 | //! # Utilities 14 | //! 15 | //! The `error` module also provides utility functions and traits for working with errors, such as formatting and error chaining. 16 | 17 | /// A structure representing a parser error. 18 | #[derive(Debug)] 19 | pub struct ParserError { 20 | source: Box, 21 | } 22 | 23 | impl ParserError { 24 | /// Creates a new `ParserError` instance with the provided error source. 25 | /// 26 | /// # Arguments 27 | /// 28 | /// * `source` - The underlying error source. 29 | /// 30 | /// # Examples 31 | /// 32 | /// ``` 33 | /// use mvt_reader::error::ParserError; 34 | /// 35 | /// let source_error = std::io::Error::new(std::io::ErrorKind::Other, "Custom error"); 36 | /// let parser_error = ParserError::new(source_error); 37 | /// ``` 38 | pub fn new(source: T) -> Self { 39 | Self { 40 | source: Box::new(source), 41 | } 42 | } 43 | } 44 | 45 | impl core::fmt::Display for ParserError { 46 | /// Formats the error message associated with the `ParserError`. 47 | /// 48 | /// # Arguments 49 | /// 50 | /// * `f` - The formatter to write the output to. 51 | /// 52 | /// # Examples 53 | /// 54 | /// ``` 55 | /// use std::error::Error; 56 | /// use mvt_reader::error::ParserError; 57 | /// 58 | /// let source_error = std::io::Error::new(std::io::ErrorKind::Other, "Custom error"); 59 | /// let parser_error = ParserError::new(source_error); 60 | /// println!("{}", parser_error); 61 | /// ``` 62 | fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { 63 | self.source.fmt(f) 64 | } 65 | } 66 | 67 | impl core::error::Error for ParserError { 68 | /// Returns the underlying source of the `ParserError`. 69 | /// 70 | /// # Examples 71 | /// 72 | /// ``` 73 | /// use std::error::Error; 74 | /// use mvt_reader::error::ParserError; 75 | /// 76 | /// let source_error = std::io::Error::new(std::io::ErrorKind::Other, "Custom error"); 77 | /// let parser_error = ParserError::new(source_error); 78 | /// let source = parser_error.source(); 79 | /// println!("Source: {}", source.unwrap()); 80 | /// ``` 81 | fn source(&self) -> Option<&(dyn core::error::Error + 'static)> { 82 | Some(self.source.as_ref()) 83 | } 84 | } 85 | 86 | /// A structure representing a version error in a vector tile. 87 | #[derive(Debug)] 88 | pub struct VersionError { 89 | layer_name: String, 90 | 91 | version: u32, 92 | } 93 | 94 | impl VersionError { 95 | /// Creates a new `VersionError` instance with the provided layer name and version. 96 | /// 97 | /// # Arguments 98 | /// 99 | /// * `layer_name` - The name of the layer. 100 | /// * `version` - The unsupported version number. 101 | /// 102 | /// # Examples 103 | /// 104 | /// ``` 105 | /// use mvt_reader::error::VersionError; 106 | /// 107 | /// let layer_name = String::from("my_layer"); 108 | /// let version = 3; 109 | /// let version_error = VersionError::new(layer_name, version); 110 | /// ``` 111 | pub fn new(layer_name: String, version: u32) -> Self { 112 | Self { 113 | layer_name, 114 | version, 115 | } 116 | } 117 | } 118 | 119 | impl core::fmt::Display for VersionError { 120 | /// Formats the error message associated with the `VersionError`. 121 | /// 122 | /// # Arguments 123 | /// 124 | /// * `f` - The formatter to write the output to. 125 | /// 126 | /// # Examples 127 | /// 128 | /// ``` 129 | /// use mvt_reader::error::VersionError; 130 | /// 131 | /// let layer_name = String::from("my_layer"); 132 | /// let version = 3; 133 | /// let version_error = VersionError::new(layer_name, version); 134 | /// println!("{}", version_error); 135 | /// ``` 136 | fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { 137 | write!( 138 | f, 139 | "Vector tile version not supported for layer `{}` (found version: {})", 140 | self.layer_name, self.version 141 | ) 142 | } 143 | } 144 | 145 | impl core::error::Error for VersionError {} 146 | 147 | /// An error indicating that the tags section of a vector tile contains errors. 148 | #[derive(Debug, Default)] 149 | pub struct TagsError; 150 | 151 | impl TagsError { 152 | /// Creates a new `TagsError` instance. 153 | /// 154 | /// # Examples 155 | /// 156 | /// ``` 157 | /// use mvt_reader::error::TagsError; 158 | /// 159 | /// let tags_error = TagsError::new(); 160 | /// ``` 161 | pub fn new() -> Self { 162 | Self 163 | } 164 | } 165 | 166 | impl core::fmt::Display for TagsError { 167 | /// Formats the error message associated with the `TagsError`. 168 | /// 169 | /// # Arguments 170 | /// 171 | /// * `f` - The formatter to write the output to. 172 | /// 173 | /// # Examples 174 | /// 175 | /// ``` 176 | /// use mvt_reader::error::TagsError; 177 | /// 178 | /// let tags_error = TagsError::new(); 179 | /// println!("{}", tags_error); 180 | /// ``` 181 | fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { 182 | write!(f, "Tags section contains errors") 183 | } 184 | } 185 | 186 | impl core::error::Error for TagsError {} 187 | 188 | /// An error indicating that the geometry section of a vector tile contains errors. 189 | #[derive(Debug, Default)] 190 | pub struct GeometryError; 191 | 192 | impl GeometryError { 193 | /// Creates a new `GeometryError` instance. 194 | /// 195 | /// # Examples 196 | /// 197 | /// ``` 198 | /// use mvt_reader::error::GeometryError; 199 | /// 200 | /// let geometry_error = GeometryError::new(); 201 | /// ``` 202 | pub fn new() -> Self { 203 | Self 204 | } 205 | } 206 | 207 | impl core::fmt::Display for GeometryError { 208 | /// Formats the error message associated with the `GeometryError`. 209 | /// 210 | /// # Arguments 211 | /// 212 | /// * `f` - The formatter to write the output to. 213 | /// 214 | /// # Examples 215 | /// 216 | /// ``` 217 | /// use mvt_reader::error::GeometryError; 218 | /// 219 | /// let geometry_error = GeometryError::new(); 220 | /// println!("{}", geometry_error); 221 | /// ``` 222 | fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { 223 | write!(f, "Geometry section contains errors") 224 | } 225 | } 226 | 227 | impl core::error::Error for GeometryError {} 228 | 229 | /// An error indicating a decoding failure during the parsing of a vector tile. 230 | #[derive(Debug)] 231 | pub struct DecodeError { 232 | source: Box, 233 | } 234 | 235 | impl DecodeError { 236 | /// Creates a new DecodeError instance with the provided decoding error from prost. 237 | /// 238 | /// # Arguments 239 | /// 240 | /// * source - The underlying decoding error from prost. 241 | pub fn new(source: Box) -> Self { 242 | Self { source } 243 | } 244 | } 245 | 246 | impl core::fmt::Display for DecodeError { 247 | /// Formats the error message associated with the `DecodeError`. 248 | /// 249 | /// # Arguments 250 | /// 251 | /// * `f` - The formatter to write the output to. 252 | fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { 253 | write!(f, "Decode error: {}", self.source) 254 | } 255 | } 256 | 257 | impl core::error::Error for DecodeError {} 258 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | //! # mvt-reader 2 | //! 3 | //! `mvt-reader` is a Rust library for decoding and reading Mapbox vector tiles. 4 | //! 5 | //! It provides the `Reader` struct, which allows you to read vector tiles and access their layers and features. 6 | //! 7 | //! # Usage 8 | //! 9 | //! To use the `mvt-reader` library in your Rust project, add the following to your `Cargo.toml` file: 10 | //! 11 | //! ```toml 12 | //! [dependencies] 13 | //! mvt-reader = "2.2.0" 14 | //! ``` 15 | //! 16 | //! Then, you can import and use the library in your code: 17 | //! 18 | //! ```rust 19 | //! use mvt_reader::{Reader, error::{ParserError}}; 20 | //! 21 | //! fn main() -> Result<(), ParserError> { 22 | //! // Read a vector tile from file or data 23 | //! let data = vec![/* Vector tile data */]; 24 | //! let reader = Reader::new(data)?; 25 | //! 26 | //! // Get layer names 27 | //! let layer_names = reader.get_layer_names()?; 28 | //! for name in layer_names { 29 | //! println!("Layer: {}", name); 30 | //! } 31 | //! 32 | //! // Get features for a specific layer 33 | //! let layer_index = 0; 34 | //! let features = reader.get_features(layer_index)?; 35 | //! for feature in features { 36 | //! todo!() 37 | //! } 38 | //! 39 | //! Ok(()) 40 | //! } 41 | //! ``` 42 | //! 43 | //! # Features 44 | //! 45 | //! The `mvt-reader` library provides the following features: 46 | //! 47 | //! - `wasm`: Enables the compilation of the library as a WebAssembly module, allowing usage in JavaScript/TypeScript projects. 48 | //! 49 | //! To enable the `wasm` feature, add the following to your `Cargo.toml` file: 50 | //! 51 | //! ```toml 52 | //! [dependencies.mvt-reader] 53 | //! version = "2.2.0" 54 | //! features = ["wasm"] 55 | //! ``` 56 | //! 57 | //! # License 58 | //! 59 | //! This project is licensed under the [MIT License](https://github.com/codeart1st/mvt-reader/blob/main/LICENSE). 60 | 61 | pub mod error; 62 | pub mod feature; 63 | pub mod layer; 64 | 65 | mod vector_tile; 66 | 67 | use feature::{Feature, Value}; 68 | use geo_types::{ 69 | Coord, Geometry, LineString, MultiLineString, MultiPoint, MultiPolygon, Point, Polygon, 70 | }; 71 | use layer::Layer; 72 | use prost::{Message, bytes::Bytes}; 73 | use vector_tile::{Tile, tile::GeomType}; 74 | 75 | /// The dimension used for the vector tile. 76 | const DIMENSION: u32 = 2; 77 | 78 | /// Reader for decoding and accessing vector tile data. 79 | pub struct Reader { 80 | tile: Tile, 81 | } 82 | 83 | impl Reader { 84 | /// Creates a new `Reader` instance with the provided vector tile data. 85 | /// 86 | /// # Arguments 87 | /// 88 | /// * `data` - The vector tile data as a byte vector. 89 | /// 90 | /// # Returns 91 | /// 92 | /// A result containing the `Reader` instance if successful, or a `DecodeError` if decoding the vector tile data fails. 93 | /// 94 | /// # Examples 95 | /// 96 | /// ``` 97 | /// use mvt_reader::Reader; 98 | /// 99 | /// let data = vec![/* Vector tile data */]; 100 | /// let reader = Reader::new(data); 101 | /// ``` 102 | pub fn new(data: Vec) -> Result { 103 | match Tile::decode(Bytes::from(data)) { 104 | Ok(tile) => Ok(Self { tile }), 105 | Err(error) => Err(error::ParserError::new(error::DecodeError::new(Box::new( 106 | error, 107 | )))), 108 | } 109 | } 110 | 111 | /// Retrieves the names of the layers in the vector tile. 112 | /// 113 | /// # Returns 114 | /// 115 | /// A result containing a vector of layer names if successful, or a `ParserError` if there is an error parsing the tile. 116 | /// 117 | /// # Examples 118 | /// 119 | /// ``` 120 | /// use mvt_reader::Reader; 121 | /// 122 | /// let data = vec![/* Vector tile data */]; 123 | /// let reader = Reader::new(data).unwrap(); 124 | /// 125 | /// match reader.get_layer_names() { 126 | /// Ok(layer_names) => { 127 | /// for name in layer_names { 128 | /// println!("Layer: {}", name); 129 | /// } 130 | /// } 131 | /// Err(error) => { 132 | /// todo!(); 133 | /// } 134 | /// } 135 | /// ``` 136 | pub fn get_layer_names(&self) -> Result, error::ParserError> { 137 | process_layers(&self.tile.layers, |layer, _| layer.name.clone()) 138 | } 139 | 140 | /// Retrieves metadata about the layers in the vector tile. 141 | /// 142 | /// # Returns 143 | /// 144 | /// A result containing a vector of `Layer` structs if successful, or a `ParserError` if there is an error parsing the tile. 145 | /// 146 | /// # Examples 147 | /// 148 | /// ``` 149 | /// use mvt_reader::Reader; 150 | /// 151 | /// let data = vec![/* Vector tile data */]; 152 | /// let reader = Reader::new(data).unwrap(); 153 | /// 154 | /// match reader.get_layer_metadata() { 155 | /// Ok(layers) => { 156 | /// for layer in layers { 157 | /// println!("Layer: {}", layer.name); 158 | /// println!("Extent: {}", layer.extent); 159 | /// } 160 | /// } 161 | /// Err(error) => { 162 | /// todo!(); 163 | /// } 164 | /// } 165 | /// ``` 166 | pub fn get_layer_metadata(&self) -> Result, error::ParserError> { 167 | process_layers(&self.tile.layers, |layer, index| Layer { 168 | layer_index: index, 169 | version: layer.version, 170 | name: layer.name.clone(), 171 | feature_count: layer.features.len(), 172 | extent: layer.extent.unwrap_or(4096), 173 | }) 174 | } 175 | 176 | /// Retrieves the features of a specific layer in the vector tile. 177 | /// 178 | /// # Arguments 179 | /// 180 | /// * `layer_index` - The index of the layer. 181 | /// 182 | /// # Returns 183 | /// 184 | /// A result containing a vector of features if successful, or a `ParserError` if there is an error parsing the tile or accessing the layer. 185 | /// 186 | /// # Examples 187 | /// 188 | /// ``` 189 | /// use mvt_reader::Reader; 190 | /// 191 | /// let data = vec![/* Vector tile data */]; 192 | /// let reader = Reader::new(data).unwrap(); 193 | /// 194 | /// match reader.get_features(0) { 195 | /// Ok(features) => { 196 | /// for feature in features { 197 | /// todo!(); 198 | /// } 199 | /// } 200 | /// Err(error) => { 201 | /// todo!(); 202 | /// } 203 | /// } 204 | /// ``` 205 | pub fn get_features(&self, layer_index: usize) -> Result, error::ParserError> { 206 | let layer = self.tile.layers.get(layer_index); 207 | match layer { 208 | Some(layer) => { 209 | let mut features = Vec::with_capacity(layer.features.len()); 210 | for feature in layer.features.iter() { 211 | if let Some(geom_type) = feature.r#type { 212 | match GeomType::try_from(geom_type) { 213 | Ok(geom_type) => { 214 | let parsed_geometry = match parse_geometry(&feature.geometry, geom_type) { 215 | Ok(parsed_geometry) => parsed_geometry, 216 | Err(error) => { 217 | return Err(error); 218 | } 219 | }; 220 | 221 | let parsed_tags = match parse_tags(&feature.tags, &layer.keys, &layer.values) { 222 | Ok(parsed_tags) => parsed_tags, 223 | Err(error) => { 224 | return Err(error); 225 | } 226 | }; 227 | 228 | features.push(Feature { 229 | geometry: parsed_geometry, 230 | id: feature.id, 231 | properties: Some(parsed_tags), 232 | }); 233 | } 234 | Err(error) => { 235 | return Err(error::ParserError::new(error::DecodeError::new(Box::new( 236 | error, 237 | )))); 238 | } 239 | } 240 | } 241 | } 242 | Ok(features) 243 | } 244 | None => Ok(vec![]), 245 | } 246 | } 247 | } 248 | 249 | fn process_layers( 250 | layers: &[vector_tile::tile::Layer], 251 | mut processor: F, 252 | ) -> Result, error::ParserError> 253 | where 254 | F: FnMut(&vector_tile::tile::Layer, usize) -> T, 255 | { 256 | let mut results = Vec::with_capacity(layers.len()); 257 | for (index, layer) in layers.iter().enumerate() { 258 | match layer.version { 259 | 1 | 2 => results.push(processor(layer, index)), 260 | _ => { 261 | return Err(error::ParserError::new(error::VersionError::new( 262 | layer.name.clone(), 263 | layer.version, 264 | ))); 265 | } 266 | } 267 | } 268 | Ok(results) 269 | } 270 | 271 | fn parse_tags( 272 | tags: &[u32], 273 | keys: &[String], 274 | values: &[vector_tile::tile::Value], 275 | ) -> Result, error::ParserError> { 276 | let mut result = std::collections::HashMap::new(); 277 | for item in tags.chunks(2) { 278 | if item.len() != 2 279 | || item[0] >= keys.len().try_into().unwrap() 280 | || item[1] >= values.len().try_into().unwrap() 281 | { 282 | return Err(error::ParserError::new(error::TagsError::new())); 283 | } 284 | result.insert( 285 | keys[item[0] as usize].clone(), 286 | map_value(values[item[1] as usize].clone()), 287 | ); 288 | } 289 | Ok(result) 290 | } 291 | 292 | fn map_value(value: vector_tile::tile::Value) -> Value { 293 | if let Some(s) = value.string_value { 294 | return Value::String(s); 295 | } 296 | if let Some(f) = value.float_value { 297 | return Value::Float(f); 298 | } 299 | if let Some(d) = value.double_value { 300 | return Value::Double(d); 301 | } 302 | if let Some(i) = value.int_value { 303 | return Value::Int(i); 304 | } 305 | if let Some(u) = value.uint_value { 306 | return Value::UInt(u); 307 | } 308 | if let Some(s) = value.sint_value { 309 | return Value::SInt(s); 310 | } 311 | if let Some(b) = value.bool_value { 312 | return Value::Bool(b); 313 | } 314 | Value::Null 315 | } 316 | 317 | fn shoelace_formula(points: &[Point]) -> f32 { 318 | let mut area: f32 = 0.0; 319 | let n = points.len(); 320 | let mut v1 = points[n - 1]; 321 | for v2 in points.iter().take(n) { 322 | area += (v2.y() - v1.y()) * (v2.x() + v1.x()); 323 | v1 = *v2; 324 | } 325 | area * 0.5 326 | } 327 | 328 | fn parse_geometry( 329 | geometry_data: &[u32], 330 | geom_type: GeomType, 331 | ) -> Result, error::ParserError> { 332 | if geom_type == GeomType::Unknown { 333 | return Err(error::ParserError::new(error::GeometryError::new())); 334 | } 335 | 336 | // worst case capacity to prevent reallocation. not needed to be exact. 337 | let mut coordinates: Vec> = Vec::with_capacity(geometry_data.len()); 338 | let mut polygons: Vec> = Vec::new(); 339 | let mut linestrings: Vec> = Vec::new(); 340 | 341 | let mut cursor: [i32; 2] = [0, 0]; 342 | let mut parameter_count: u32 = 0; 343 | 344 | for value in geometry_data.iter() { 345 | if parameter_count == 0 { 346 | let command_integer = value; 347 | let id = (command_integer & 0x7) as u8; 348 | match id { 349 | 1 => { 350 | // MoveTo 351 | parameter_count = (command_integer >> 3) * DIMENSION; 352 | if geom_type == GeomType::Linestring && !coordinates.is_empty() { 353 | linestrings.push(LineString::new(coordinates)); 354 | // start with a new linestring 355 | coordinates = Vec::with_capacity(geometry_data.len()); 356 | } 357 | } 358 | 2 => { 359 | // LineTo 360 | parameter_count = (command_integer >> 3) * DIMENSION; 361 | } 362 | 7 => { 363 | // ClosePath 364 | let first_coordinate = match coordinates.first() { 365 | Some(coord) => coord.to_owned(), 366 | None => { 367 | return Err(error::ParserError::new(error::GeometryError::new())); 368 | } 369 | }; 370 | coordinates.push(first_coordinate); 371 | 372 | let ring = LineString::new(coordinates); 373 | 374 | let area = shoelace_formula(&ring.clone().into_points()); 375 | 376 | if area > 0.0 { 377 | // exterior ring 378 | if !linestrings.is_empty() { 379 | // finish previous geometry 380 | polygons.push(Polygon::new( 381 | linestrings[0].clone(), 382 | linestrings[1..].into(), 383 | )); 384 | linestrings = Vec::new(); 385 | } 386 | } 387 | 388 | linestrings.push(ring); 389 | // start a new sequence 390 | coordinates = Vec::with_capacity(geometry_data.len()); 391 | } 392 | _ => (), 393 | } 394 | } else { 395 | let parameter_integer = value; 396 | let integer_value = ((parameter_integer >> 1) as i32) ^ -((parameter_integer & 1) as i32); 397 | if parameter_count.is_multiple_of(DIMENSION) { 398 | cursor[0] = match cursor[0].checked_add(integer_value) { 399 | Some(result) => result, 400 | None => i32::MAX, // clip value 401 | }; 402 | } else { 403 | cursor[1] = match cursor[1].checked_add(integer_value) { 404 | Some(result) => result, 405 | None => i32::MAX, // clip value 406 | }; 407 | coordinates.push(Coord { 408 | x: cursor[0] as f32, 409 | y: cursor[1] as f32, 410 | }); 411 | } 412 | parameter_count -= 1; 413 | } 414 | } 415 | 416 | match geom_type { 417 | GeomType::Linestring => { 418 | // the last linestring is in coordinates vec 419 | if !linestrings.is_empty() { 420 | linestrings.push(LineString::new(coordinates)); 421 | return Ok(MultiLineString::new(linestrings).into()); 422 | } 423 | Ok(LineString::new(coordinates).into()) 424 | } 425 | GeomType::Point => Ok( 426 | MultiPoint( 427 | coordinates 428 | .iter() 429 | .map(|coord| Point::new(coord.x, coord.y)) 430 | .collect(), 431 | ) 432 | .into(), 433 | ), 434 | GeomType::Polygon => { 435 | if !linestrings.is_empty() { 436 | // finish pending polygon 437 | polygons.push(Polygon::new( 438 | linestrings[0].clone(), 439 | linestrings[1..].into(), 440 | )); 441 | return Ok(MultiPolygon::new(polygons).into()); 442 | } 443 | match polygons.first() { 444 | Some(polygon) => Ok(polygon.to_owned().into()), 445 | None => Err(error::ParserError::new(error::GeometryError::new())), 446 | } 447 | } 448 | GeomType::Unknown => Err(error::ParserError::new(error::GeometryError::new())), 449 | } 450 | } 451 | 452 | #[cfg(feature = "wasm")] 453 | pub mod wasm { 454 | 455 | use crate::feature::Value; 456 | use geojson::{Feature, GeoJson, JsonObject, JsonValue, feature::Id}; 457 | use serde::ser::{Serialize, SerializeStruct}; 458 | use serde_wasm_bindgen::Serializer; 459 | use wasm_bindgen::prelude::*; 460 | 461 | /// Converts a `Value` into a `serde_json::Value`. 462 | impl From for JsonValue { 463 | fn from(value: Value) -> Self { 464 | match value { 465 | Value::Null => JsonValue::Null, 466 | Value::Bool(b) => JsonValue::from(b), 467 | Value::Int(i) => JsonValue::from(i), 468 | Value::UInt(u) => JsonValue::from(u), 469 | Value::SInt(s) => JsonValue::from(s), 470 | Value::Float(f) => JsonValue::from(f), 471 | Value::Double(d) => JsonValue::from(d), 472 | Value::String(s) => JsonValue::from(s), 473 | } 474 | } 475 | } 476 | 477 | /// Converts a `super::feature::Feature` into a `wasm_bindgen::JsValue`. 478 | impl From for wasm_bindgen::JsValue { 479 | fn from(feature: super::feature::Feature) -> Self { 480 | let properties: Option = feature.properties.as_ref().map(|props| { 481 | props 482 | .clone() 483 | .into_iter() 484 | .map(|(k, v)| (k, v.into())) 485 | .collect() 486 | }); 487 | 488 | let geojson = GeoJson::Feature(Feature { 489 | bbox: None, 490 | geometry: Some(feature.get_geometry().into()), 491 | id: feature.id.map(|id| Id::Number(id.into())), 492 | properties, 493 | foreign_members: None, 494 | }); 495 | 496 | geojson.serialize(&Serializer::json_compatible()).unwrap() 497 | } 498 | } 499 | 500 | /// Converts a `super::layer::Layer` into a `wasm_bindgen::JsValue`. 501 | impl From for wasm_bindgen::JsValue { 502 | fn from(layer: super::layer::Layer) -> Self { 503 | layer.serialize(&Serializer::json_compatible()).unwrap() 504 | } 505 | } 506 | 507 | impl Serialize for super::layer::Layer { 508 | fn serialize(&self, serializer: S) -> Result 509 | where 510 | S: serde::ser::Serializer, 511 | { 512 | let mut state = serializer.serialize_struct("Layer", 5)?; 513 | state.serialize_field("layer_index", &self.layer_index)?; 514 | state.serialize_field("version", &self.version)?; 515 | state.serialize_field("name", &self.name)?; 516 | state.serialize_field("feature_count", &self.feature_count)?; 517 | state.serialize_field("extent", &self.extent)?; 518 | state.end() 519 | } 520 | } 521 | 522 | /// Reader for decoding and accessing vector tile data in WebAssembly. 523 | #[wasm_bindgen] 524 | pub struct Reader { 525 | reader: Option, 526 | } 527 | 528 | #[wasm_bindgen] 529 | impl Reader { 530 | /// Creates a new `Reader` instance with the provided vector tile data. 531 | /// 532 | /// # Arguments 533 | /// 534 | /// * `data` - The vector tile data as a `Vec`. 535 | /// * `error_callback` - An optional JavaScript callback function to handle errors. It should accept a single parameter which will contain the error message as a string. 536 | /// 537 | /// # Examples 538 | /// 539 | /// ``` 540 | /// let tileData = getVectorTileData(); 541 | /// let reader = new Reader(tileData, handleErrors); 542 | /// ``` 543 | #[wasm_bindgen(constructor)] 544 | pub fn new(data: Vec, error_callback: Option) -> Reader { 545 | let reader = match super::Reader::new(data) { 546 | Ok(reader) => Some(reader), 547 | Err(error) => { 548 | if let Some(callback) = error_callback { 549 | callback 550 | .call1(&JsValue::NULL, &JsValue::from_str(&format!("{:?}", error))) 551 | .unwrap(); 552 | } 553 | None 554 | } 555 | }; 556 | Reader { reader } 557 | } 558 | 559 | /// Retrieves the layer names present in the vector tile. 560 | /// 561 | /// # Arguments 562 | /// 563 | /// * `error_callback` - An optional JavaScript callback function to handle errors. It should accept a single parameter which will contain the error message as a string. 564 | /// 565 | /// # Returns 566 | /// 567 | /// A JavaScript array containing the layer names as strings. 568 | /// 569 | /// # Examples 570 | /// 571 | /// ``` 572 | /// let layerNames = reader.getLayerNames(handleErrors); 573 | /// for (let i = 0; i < layerNames.length; i++) { 574 | /// console.log(layerNames[i]); 575 | /// } 576 | /// ``` 577 | #[wasm_bindgen(js_name = getLayerNames)] 578 | pub fn get_layer_names(&self, error_callback: Option) -> JsValue { 579 | self.handle_result(|reader| reader.get_layer_names(), error_callback) 580 | } 581 | 582 | /// Retrieves the layer metadata present in the vector tile. 583 | /// 584 | /// # Arguments 585 | /// 586 | /// * `error_callback` - An optional JavaScript callback function to handle errors. It should accept a single parameter which will contain the error message as a string. 587 | /// 588 | /// # Returns 589 | /// 590 | /// A JavaScript array containing the layer metadata as objects. 591 | /// 592 | /// # Examples 593 | /// 594 | /// ``` 595 | /// let layers = reader.getLayerMetadata(handleErrors); 596 | /// for (let i = 0; i < layers.length; i++) { 597 | /// console.log(layers[i].name); 598 | /// } 599 | /// ``` 600 | #[wasm_bindgen(js_name = getLayerMetadata)] 601 | pub fn get_layer_metadata(&self, error_callback: Option) -> JsValue { 602 | self.handle_result(|reader| reader.get_layer_metadata(), error_callback) 603 | } 604 | 605 | /// Retrieves the features of a specific layer in the vector tile. 606 | /// 607 | /// # Arguments 608 | /// 609 | /// * `layer_index` - The index of the layer to retrieve features from. 610 | /// * `error_callback` - An optional JavaScript callback function to handle errors. It should accept a single parameter which will contain the error message as a string. 611 | /// 612 | /// # Returns 613 | /// 614 | /// A JavaScript array containing the features as GeoJSON objects. 615 | /// 616 | /// # Examples 617 | /// 618 | /// ``` 619 | /// let features = reader.getFeatures(0, handleErrors); 620 | /// for (let i = 0; i < features.length; i++) { 621 | /// console.log(features[i]); 622 | /// } 623 | /// ``` 624 | #[wasm_bindgen(js_name = getFeatures)] 625 | pub fn get_features( 626 | &self, 627 | layer_index: usize, 628 | error_callback: Option, 629 | ) -> JsValue { 630 | self.handle_result(|reader| reader.get_features(layer_index), error_callback) 631 | } 632 | 633 | fn handle_result(&self, operation: F, error_callback: Option) -> JsValue 634 | where 635 | T: IntoIterator, 636 | T::Item: Into, 637 | F: FnOnce(&super::Reader) -> Result, 638 | { 639 | match &self.reader { 640 | Some(reader) => match operation(reader) { 641 | Ok(result) => result 642 | .into_iter() 643 | .map(Into::into) 644 | .collect::() 645 | .into(), 646 | Err(error) => { 647 | if let Some(callback) = error_callback { 648 | callback 649 | .call1(&JsValue::NULL, &JsValue::from_str(&format!("{:?}", error))) 650 | .unwrap(); 651 | } 652 | JsValue::NULL 653 | } 654 | }, 655 | None => JsValue::NULL, 656 | } 657 | } 658 | } 659 | } 660 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 4 4 | 5 | [[package]] 6 | name = "aho-corasick" 7 | version = "1.1.2" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" 10 | dependencies = [ 11 | "memchr", 12 | ] 13 | 14 | [[package]] 15 | name = "anyhow" 16 | version = "1.0.75" 17 | source = "registry+https://github.com/rust-lang/crates.io-index" 18 | checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6" 19 | 20 | [[package]] 21 | name = "approx" 22 | version = "0.5.1" 23 | source = "registry+https://github.com/rust-lang/crates.io-index" 24 | checksum = "cab112f0a86d568ea0e627cc1d6be74a1e9cd55214684db5561995f6dad897c6" 25 | dependencies = [ 26 | "num-traits", 27 | ] 28 | 29 | [[package]] 30 | name = "as-slice" 31 | version = "0.1.5" 32 | source = "registry+https://github.com/rust-lang/crates.io-index" 33 | checksum = "45403b49e3954a4b8428a0ac21a4b7afadccf92bfd96273f1a58cd4812496ae0" 34 | dependencies = [ 35 | "generic-array 0.12.4", 36 | "generic-array 0.13.3", 37 | "generic-array 0.14.9", 38 | "stable_deref_trait", 39 | ] 40 | 41 | [[package]] 42 | name = "atomic-polyfill" 43 | version = "1.0.3" 44 | source = "registry+https://github.com/rust-lang/crates.io-index" 45 | checksum = "8cf2bce30dfe09ef0bfaef228b9d414faaf7e563035494d7fe092dba54b300f4" 46 | dependencies = [ 47 | "critical-section", 48 | ] 49 | 50 | [[package]] 51 | name = "autocfg" 52 | version = "1.1.0" 53 | source = "registry+https://github.com/rust-lang/crates.io-index" 54 | checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" 55 | 56 | [[package]] 57 | name = "bitflags" 58 | version = "1.3.2" 59 | source = "registry+https://github.com/rust-lang/crates.io-index" 60 | checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" 61 | 62 | [[package]] 63 | name = "bitflags" 64 | version = "2.4.1" 65 | source = "registry+https://github.com/rust-lang/crates.io-index" 66 | checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" 67 | 68 | [[package]] 69 | name = "bumpalo" 70 | version = "3.14.0" 71 | source = "registry+https://github.com/rust-lang/crates.io-index" 72 | checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec" 73 | 74 | [[package]] 75 | name = "byteorder" 76 | version = "1.5.0" 77 | source = "registry+https://github.com/rust-lang/crates.io-index" 78 | checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" 79 | 80 | [[package]] 81 | name = "bytes" 82 | version = "1.5.0" 83 | source = "registry+https://github.com/rust-lang/crates.io-index" 84 | checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" 85 | 86 | [[package]] 87 | name = "cfg-if" 88 | version = "1.0.0" 89 | source = "registry+https://github.com/rust-lang/crates.io-index" 90 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 91 | 92 | [[package]] 93 | name = "critical-section" 94 | version = "1.2.0" 95 | source = "registry+https://github.com/rust-lang/crates.io-index" 96 | checksum = "790eea4361631c5e7d22598ecd5723ff611904e3344ce8720784c93e3d83d40b" 97 | 98 | [[package]] 99 | name = "either" 100 | version = "1.9.0" 101 | source = "registry+https://github.com/rust-lang/crates.io-index" 102 | checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" 103 | 104 | [[package]] 105 | name = "equivalent" 106 | version = "1.0.1" 107 | source = "registry+https://github.com/rust-lang/crates.io-index" 108 | checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" 109 | 110 | [[package]] 111 | name = "errno" 112 | version = "0.3.5" 113 | source = "registry+https://github.com/rust-lang/crates.io-index" 114 | checksum = "ac3e13f66a2f95e32a39eaa81f6b95d42878ca0e1db0c7543723dfe12557e860" 115 | dependencies = [ 116 | "libc", 117 | "windows-sys", 118 | ] 119 | 120 | [[package]] 121 | name = "fastrand" 122 | version = "2.0.1" 123 | source = "registry+https://github.com/rust-lang/crates.io-index" 124 | checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5" 125 | 126 | [[package]] 127 | name = "fixedbitset" 128 | version = "0.4.2" 129 | source = "registry+https://github.com/rust-lang/crates.io-index" 130 | checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" 131 | 132 | [[package]] 133 | name = "generic-array" 134 | version = "0.12.4" 135 | source = "registry+https://github.com/rust-lang/crates.io-index" 136 | checksum = "ffdf9f34f1447443d37393cc6c2b8313aebddcd96906caf34e54c68d8e57d7bd" 137 | dependencies = [ 138 | "typenum", 139 | ] 140 | 141 | [[package]] 142 | name = "generic-array" 143 | version = "0.13.3" 144 | source = "registry+https://github.com/rust-lang/crates.io-index" 145 | checksum = "f797e67af32588215eaaab8327027ee8e71b9dd0b2b26996aedf20c030fce309" 146 | dependencies = [ 147 | "typenum", 148 | ] 149 | 150 | [[package]] 151 | name = "generic-array" 152 | version = "0.14.9" 153 | source = "registry+https://github.com/rust-lang/crates.io-index" 154 | checksum = "4bb6743198531e02858aeaea5398fcc883e71851fcbcb5a2f773e2fb6cb1edf2" 155 | dependencies = [ 156 | "typenum", 157 | "version_check", 158 | ] 159 | 160 | [[package]] 161 | name = "geo-types" 162 | version = "0.7.18" 163 | source = "registry+https://github.com/rust-lang/crates.io-index" 164 | checksum = "24f8647af4005fa11da47cd56252c6ef030be8fa97bdbf355e7dfb6348f0a82c" 165 | dependencies = [ 166 | "approx", 167 | "num-traits", 168 | "rstar 0.10.0", 169 | "rstar 0.11.0", 170 | "rstar 0.12.2", 171 | "rstar 0.8.4", 172 | "rstar 0.9.3", 173 | "serde", 174 | ] 175 | 176 | [[package]] 177 | name = "geojson" 178 | version = "0.24.2" 179 | source = "registry+https://github.com/rust-lang/crates.io-index" 180 | checksum = "e26f3c45b36fccc9cf2805e61d4da6bc4bbd5a3a9589b01afa3a40eff703bd79" 181 | dependencies = [ 182 | "geo-types", 183 | "log", 184 | "serde", 185 | "serde_json", 186 | "thiserror", 187 | ] 188 | 189 | [[package]] 190 | name = "hash32" 191 | version = "0.1.1" 192 | source = "registry+https://github.com/rust-lang/crates.io-index" 193 | checksum = "d4041af86e63ac4298ce40e5cca669066e75b6f1aa3390fe2561ffa5e1d9f4cc" 194 | dependencies = [ 195 | "byteorder", 196 | ] 197 | 198 | [[package]] 199 | name = "hash32" 200 | version = "0.2.1" 201 | source = "registry+https://github.com/rust-lang/crates.io-index" 202 | checksum = "b0c35f58762feb77d74ebe43bdbc3210f09be9fe6742234d573bacc26ed92b67" 203 | dependencies = [ 204 | "byteorder", 205 | ] 206 | 207 | [[package]] 208 | name = "hash32" 209 | version = "0.3.1" 210 | source = "registry+https://github.com/rust-lang/crates.io-index" 211 | checksum = "47d60b12902ba28e2730cd37e95b8c9223af2808df9e902d4df49588d1470606" 212 | dependencies = [ 213 | "byteorder", 214 | ] 215 | 216 | [[package]] 217 | name = "hashbrown" 218 | version = "0.14.2" 219 | source = "registry+https://github.com/rust-lang/crates.io-index" 220 | checksum = "f93e7192158dbcda357bdec5fb5788eebf8bbac027f3f33e719d29135ae84156" 221 | 222 | [[package]] 223 | name = "heapless" 224 | version = "0.6.1" 225 | source = "registry+https://github.com/rust-lang/crates.io-index" 226 | checksum = "634bd4d29cbf24424d0a4bfcbf80c6960129dc24424752a7d1d1390607023422" 227 | dependencies = [ 228 | "as-slice", 229 | "generic-array 0.14.9", 230 | "hash32 0.1.1", 231 | "stable_deref_trait", 232 | ] 233 | 234 | [[package]] 235 | name = "heapless" 236 | version = "0.7.17" 237 | source = "registry+https://github.com/rust-lang/crates.io-index" 238 | checksum = "cdc6457c0eb62c71aac4bc17216026d8410337c4126773b9c5daba343f17964f" 239 | dependencies = [ 240 | "atomic-polyfill", 241 | "hash32 0.2.1", 242 | "rustc_version", 243 | "spin", 244 | "stable_deref_trait", 245 | ] 246 | 247 | [[package]] 248 | name = "heapless" 249 | version = "0.8.0" 250 | source = "registry+https://github.com/rust-lang/crates.io-index" 251 | checksum = "0bfb9eb618601c89945a70e254898da93b13be0388091d42117462b265bb3fad" 252 | dependencies = [ 253 | "hash32 0.3.1", 254 | "stable_deref_trait", 255 | ] 256 | 257 | [[package]] 258 | name = "heck" 259 | version = "0.4.1" 260 | source = "registry+https://github.com/rust-lang/crates.io-index" 261 | checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" 262 | 263 | [[package]] 264 | name = "indexmap" 265 | version = "2.0.2" 266 | source = "registry+https://github.com/rust-lang/crates.io-index" 267 | checksum = "8adf3ddd720272c6ea8bf59463c04e0f93d0bbf7c5439b691bca2987e0270897" 268 | dependencies = [ 269 | "equivalent", 270 | "hashbrown", 271 | ] 272 | 273 | [[package]] 274 | name = "itertools" 275 | version = "0.11.0" 276 | source = "registry+https://github.com/rust-lang/crates.io-index" 277 | checksum = "b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57" 278 | dependencies = [ 279 | "either", 280 | ] 281 | 282 | [[package]] 283 | name = "itoa" 284 | version = "1.0.9" 285 | source = "registry+https://github.com/rust-lang/crates.io-index" 286 | checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" 287 | 288 | [[package]] 289 | name = "js-sys" 290 | version = "0.3.83" 291 | source = "registry+https://github.com/rust-lang/crates.io-index" 292 | checksum = "464a3709c7f55f1f721e5389aa6ea4e3bc6aba669353300af094b29ffbdde1d8" 293 | dependencies = [ 294 | "once_cell", 295 | "wasm-bindgen", 296 | ] 297 | 298 | [[package]] 299 | name = "libc" 300 | version = "0.2.149" 301 | source = "registry+https://github.com/rust-lang/crates.io-index" 302 | checksum = "a08173bc88b7955d1b3145aa561539096c421ac8debde8cbc3612ec635fee29b" 303 | 304 | [[package]] 305 | name = "libm" 306 | version = "0.2.8" 307 | source = "registry+https://github.com/rust-lang/crates.io-index" 308 | checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" 309 | 310 | [[package]] 311 | name = "linux-raw-sys" 312 | version = "0.4.10" 313 | source = "registry+https://github.com/rust-lang/crates.io-index" 314 | checksum = "da2479e8c062e40bf0066ffa0bc823de0a9368974af99c9f6df941d2c231e03f" 315 | 316 | [[package]] 317 | name = "lock_api" 318 | version = "0.4.14" 319 | source = "registry+https://github.com/rust-lang/crates.io-index" 320 | checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" 321 | dependencies = [ 322 | "scopeguard", 323 | ] 324 | 325 | [[package]] 326 | name = "log" 327 | version = "0.4.20" 328 | source = "registry+https://github.com/rust-lang/crates.io-index" 329 | checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" 330 | 331 | [[package]] 332 | name = "memchr" 333 | version = "2.6.4" 334 | source = "registry+https://github.com/rust-lang/crates.io-index" 335 | checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167" 336 | 337 | [[package]] 338 | name = "multimap" 339 | version = "0.8.3" 340 | source = "registry+https://github.com/rust-lang/crates.io-index" 341 | checksum = "e5ce46fe64a9d73be07dcbe690a38ce1b293be448fd8ce1e6c1b8062c9f72c6a" 342 | 343 | [[package]] 344 | name = "mvt-reader" 345 | version = "2.1.0" 346 | dependencies = [ 347 | "geo-types", 348 | "geojson", 349 | "js-sys", 350 | "prost 0.13.5", 351 | "prost-build", 352 | "serde", 353 | "serde-wasm-bindgen", 354 | "serde_json", 355 | "wasm-bindgen", 356 | ] 357 | 358 | [[package]] 359 | name = "num-traits" 360 | version = "0.2.17" 361 | source = "registry+https://github.com/rust-lang/crates.io-index" 362 | checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c" 363 | dependencies = [ 364 | "autocfg", 365 | "libm", 366 | ] 367 | 368 | [[package]] 369 | name = "once_cell" 370 | version = "1.18.0" 371 | source = "registry+https://github.com/rust-lang/crates.io-index" 372 | checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" 373 | 374 | [[package]] 375 | name = "pdqselect" 376 | version = "0.1.0" 377 | source = "registry+https://github.com/rust-lang/crates.io-index" 378 | checksum = "4ec91767ecc0a0bbe558ce8c9da33c068066c57ecc8bb8477ef8c1ad3ef77c27" 379 | 380 | [[package]] 381 | name = "petgraph" 382 | version = "0.6.4" 383 | source = "registry+https://github.com/rust-lang/crates.io-index" 384 | checksum = "e1d3afd2628e69da2be385eb6f2fd57c8ac7977ceeff6dc166ff1657b0e386a9" 385 | dependencies = [ 386 | "fixedbitset", 387 | "indexmap", 388 | ] 389 | 390 | [[package]] 391 | name = "prettyplease" 392 | version = "0.2.15" 393 | source = "registry+https://github.com/rust-lang/crates.io-index" 394 | checksum = "ae005bd773ab59b4725093fd7df83fd7892f7d8eafb48dbd7de6e024e4215f9d" 395 | dependencies = [ 396 | "proc-macro2", 397 | "syn", 398 | ] 399 | 400 | [[package]] 401 | name = "proc-macro2" 402 | version = "1.0.93" 403 | source = "registry+https://github.com/rust-lang/crates.io-index" 404 | checksum = "60946a68e5f9d28b0dc1c21bb8a97ee7d018a8b322fa57838ba31cc878e22d99" 405 | dependencies = [ 406 | "unicode-ident", 407 | ] 408 | 409 | [[package]] 410 | name = "prost" 411 | version = "0.13.5" 412 | source = "registry+https://github.com/rust-lang/crates.io-index" 413 | checksum = "2796faa41db3ec313a31f7624d9286acf277b52de526150b7e69f3debf891ee5" 414 | dependencies = [ 415 | "bytes", 416 | "prost-derive 0.13.5", 417 | ] 418 | 419 | [[package]] 420 | name = "prost" 421 | version = "0.14.1" 422 | source = "registry+https://github.com/rust-lang/crates.io-index" 423 | checksum = "7231bd9b3d3d33c86b58adbac74b5ec0ad9f496b19d22801d773636feaa95f3d" 424 | dependencies = [ 425 | "bytes", 426 | "prost-derive 0.14.1", 427 | ] 428 | 429 | [[package]] 430 | name = "prost-build" 431 | version = "0.14.1" 432 | source = "registry+https://github.com/rust-lang/crates.io-index" 433 | checksum = "ac6c3320f9abac597dcbc668774ef006702672474aad53c6d596b62e487b40b1" 434 | dependencies = [ 435 | "heck", 436 | "itertools", 437 | "log", 438 | "multimap", 439 | "once_cell", 440 | "petgraph", 441 | "prettyplease", 442 | "prost 0.14.1", 443 | "prost-types", 444 | "regex", 445 | "syn", 446 | "tempfile", 447 | ] 448 | 449 | [[package]] 450 | name = "prost-derive" 451 | version = "0.13.5" 452 | source = "registry+https://github.com/rust-lang/crates.io-index" 453 | checksum = "8a56d757972c98b346a9b766e3f02746cde6dd1cd1d1d563472929fdd74bec4d" 454 | dependencies = [ 455 | "anyhow", 456 | "itertools", 457 | "proc-macro2", 458 | "quote", 459 | "syn", 460 | ] 461 | 462 | [[package]] 463 | name = "prost-derive" 464 | version = "0.14.1" 465 | source = "registry+https://github.com/rust-lang/crates.io-index" 466 | checksum = "9120690fafc389a67ba3803df527d0ec9cbbc9cc45e4cc20b332996dfb672425" 467 | dependencies = [ 468 | "anyhow", 469 | "itertools", 470 | "proc-macro2", 471 | "quote", 472 | "syn", 473 | ] 474 | 475 | [[package]] 476 | name = "prost-types" 477 | version = "0.14.1" 478 | source = "registry+https://github.com/rust-lang/crates.io-index" 479 | checksum = "b9b4db3d6da204ed77bb26ba83b6122a73aeb2e87e25fbf7ad2e84c4ccbf8f72" 480 | dependencies = [ 481 | "prost 0.14.1", 482 | ] 483 | 484 | [[package]] 485 | name = "quote" 486 | version = "1.0.35" 487 | source = "registry+https://github.com/rust-lang/crates.io-index" 488 | checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" 489 | dependencies = [ 490 | "proc-macro2", 491 | ] 492 | 493 | [[package]] 494 | name = "redox_syscall" 495 | version = "0.3.5" 496 | source = "registry+https://github.com/rust-lang/crates.io-index" 497 | checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" 498 | dependencies = [ 499 | "bitflags 1.3.2", 500 | ] 501 | 502 | [[package]] 503 | name = "regex" 504 | version = "1.10.2" 505 | source = "registry+https://github.com/rust-lang/crates.io-index" 506 | checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343" 507 | dependencies = [ 508 | "aho-corasick", 509 | "memchr", 510 | "regex-automata", 511 | "regex-syntax", 512 | ] 513 | 514 | [[package]] 515 | name = "regex-automata" 516 | version = "0.4.3" 517 | source = "registry+https://github.com/rust-lang/crates.io-index" 518 | checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f" 519 | dependencies = [ 520 | "aho-corasick", 521 | "memchr", 522 | "regex-syntax", 523 | ] 524 | 525 | [[package]] 526 | name = "regex-syntax" 527 | version = "0.8.2" 528 | source = "registry+https://github.com/rust-lang/crates.io-index" 529 | checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" 530 | 531 | [[package]] 532 | name = "rstar" 533 | version = "0.8.4" 534 | source = "registry+https://github.com/rust-lang/crates.io-index" 535 | checksum = "3a45c0e8804d37e4d97e55c6f258bc9ad9c5ee7b07437009dd152d764949a27c" 536 | dependencies = [ 537 | "heapless 0.6.1", 538 | "num-traits", 539 | "pdqselect", 540 | "serde", 541 | "smallvec", 542 | ] 543 | 544 | [[package]] 545 | name = "rstar" 546 | version = "0.9.3" 547 | source = "registry+https://github.com/rust-lang/crates.io-index" 548 | checksum = "b40f1bfe5acdab44bc63e6699c28b74f75ec43afb59f3eda01e145aff86a25fa" 549 | dependencies = [ 550 | "heapless 0.7.17", 551 | "num-traits", 552 | "serde", 553 | "smallvec", 554 | ] 555 | 556 | [[package]] 557 | name = "rstar" 558 | version = "0.10.0" 559 | source = "registry+https://github.com/rust-lang/crates.io-index" 560 | checksum = "1f39465655a1e3d8ae79c6d9e007f4953bfc5d55297602df9dc38f9ae9f1359a" 561 | dependencies = [ 562 | "heapless 0.7.17", 563 | "num-traits", 564 | "serde", 565 | "smallvec", 566 | ] 567 | 568 | [[package]] 569 | name = "rstar" 570 | version = "0.11.0" 571 | source = "registry+https://github.com/rust-lang/crates.io-index" 572 | checksum = "73111312eb7a2287d229f06c00ff35b51ddee180f017ab6dec1f69d62ac098d6" 573 | dependencies = [ 574 | "heapless 0.7.17", 575 | "num-traits", 576 | "serde", 577 | "smallvec", 578 | ] 579 | 580 | [[package]] 581 | name = "rstar" 582 | version = "0.12.2" 583 | source = "registry+https://github.com/rust-lang/crates.io-index" 584 | checksum = "421400d13ccfd26dfa5858199c30a5d76f9c54e0dba7575273025b43c5175dbb" 585 | dependencies = [ 586 | "heapless 0.8.0", 587 | "num-traits", 588 | "serde", 589 | "smallvec", 590 | ] 591 | 592 | [[package]] 593 | name = "rustc_version" 594 | version = "0.4.1" 595 | source = "registry+https://github.com/rust-lang/crates.io-index" 596 | checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" 597 | dependencies = [ 598 | "semver", 599 | ] 600 | 601 | [[package]] 602 | name = "rustix" 603 | version = "0.38.20" 604 | source = "registry+https://github.com/rust-lang/crates.io-index" 605 | checksum = "67ce50cb2e16c2903e30d1cbccfd8387a74b9d4c938b6a4c5ec6cc7556f7a8a0" 606 | dependencies = [ 607 | "bitflags 2.4.1", 608 | "errno", 609 | "libc", 610 | "linux-raw-sys", 611 | "windows-sys", 612 | ] 613 | 614 | [[package]] 615 | name = "rustversion" 616 | version = "1.0.19" 617 | source = "registry+https://github.com/rust-lang/crates.io-index" 618 | checksum = "f7c45b9784283f1b2e7fb61b42047c2fd678ef0960d4f6f1eba131594cc369d4" 619 | 620 | [[package]] 621 | name = "ryu" 622 | version = "1.0.15" 623 | source = "registry+https://github.com/rust-lang/crates.io-index" 624 | checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" 625 | 626 | [[package]] 627 | name = "scopeguard" 628 | version = "1.2.0" 629 | source = "registry+https://github.com/rust-lang/crates.io-index" 630 | checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" 631 | 632 | [[package]] 633 | name = "semver" 634 | version = "1.0.27" 635 | source = "registry+https://github.com/rust-lang/crates.io-index" 636 | checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2" 637 | 638 | [[package]] 639 | name = "serde" 640 | version = "1.0.228" 641 | source = "registry+https://github.com/rust-lang/crates.io-index" 642 | checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" 643 | dependencies = [ 644 | "serde_core", 645 | "serde_derive", 646 | ] 647 | 648 | [[package]] 649 | name = "serde-wasm-bindgen" 650 | version = "0.6.5" 651 | source = "registry+https://github.com/rust-lang/crates.io-index" 652 | checksum = "8302e169f0eddcc139c70f139d19d6467353af16f9fce27e8c30158036a1e16b" 653 | dependencies = [ 654 | "js-sys", 655 | "serde", 656 | "wasm-bindgen", 657 | ] 658 | 659 | [[package]] 660 | name = "serde_core" 661 | version = "1.0.228" 662 | source = "registry+https://github.com/rust-lang/crates.io-index" 663 | checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" 664 | dependencies = [ 665 | "serde_derive", 666 | ] 667 | 668 | [[package]] 669 | name = "serde_derive" 670 | version = "1.0.228" 671 | source = "registry+https://github.com/rust-lang/crates.io-index" 672 | checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" 673 | dependencies = [ 674 | "proc-macro2", 675 | "quote", 676 | "syn", 677 | ] 678 | 679 | [[package]] 680 | name = "serde_json" 681 | version = "1.0.145" 682 | source = "registry+https://github.com/rust-lang/crates.io-index" 683 | checksum = "402a6f66d8c709116cf22f558eab210f5a50187f702eb4d7e5ef38d9a7f1c79c" 684 | dependencies = [ 685 | "itoa", 686 | "memchr", 687 | "ryu", 688 | "serde", 689 | "serde_core", 690 | ] 691 | 692 | [[package]] 693 | name = "smallvec" 694 | version = "1.15.1" 695 | source = "registry+https://github.com/rust-lang/crates.io-index" 696 | checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" 697 | 698 | [[package]] 699 | name = "spin" 700 | version = "0.9.8" 701 | source = "registry+https://github.com/rust-lang/crates.io-index" 702 | checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" 703 | dependencies = [ 704 | "lock_api", 705 | ] 706 | 707 | [[package]] 708 | name = "stable_deref_trait" 709 | version = "1.2.1" 710 | source = "registry+https://github.com/rust-lang/crates.io-index" 711 | checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" 712 | 713 | [[package]] 714 | name = "syn" 715 | version = "2.0.98" 716 | source = "registry+https://github.com/rust-lang/crates.io-index" 717 | checksum = "36147f1a48ae0ec2b5b3bc5b537d267457555a10dc06f3dbc8cb11ba3006d3b1" 718 | dependencies = [ 719 | "proc-macro2", 720 | "quote", 721 | "unicode-ident", 722 | ] 723 | 724 | [[package]] 725 | name = "tempfile" 726 | version = "3.8.0" 727 | source = "registry+https://github.com/rust-lang/crates.io-index" 728 | checksum = "cb94d2f3cc536af71caac6b6fcebf65860b347e7ce0cc9ebe8f70d3e521054ef" 729 | dependencies = [ 730 | "cfg-if", 731 | "fastrand", 732 | "redox_syscall", 733 | "rustix", 734 | "windows-sys", 735 | ] 736 | 737 | [[package]] 738 | name = "thiserror" 739 | version = "2.0.11" 740 | source = "registry+https://github.com/rust-lang/crates.io-index" 741 | checksum = "d452f284b73e6d76dd36758a0c8684b1d5be31f92b89d07fd5822175732206fc" 742 | dependencies = [ 743 | "thiserror-impl", 744 | ] 745 | 746 | [[package]] 747 | name = "thiserror-impl" 748 | version = "2.0.11" 749 | source = "registry+https://github.com/rust-lang/crates.io-index" 750 | checksum = "26afc1baea8a989337eeb52b6e72a039780ce45c3edfcc9c5b9d112feeb173c2" 751 | dependencies = [ 752 | "proc-macro2", 753 | "quote", 754 | "syn", 755 | ] 756 | 757 | [[package]] 758 | name = "typenum" 759 | version = "1.19.0" 760 | source = "registry+https://github.com/rust-lang/crates.io-index" 761 | checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" 762 | 763 | [[package]] 764 | name = "unicode-ident" 765 | version = "1.0.12" 766 | source = "registry+https://github.com/rust-lang/crates.io-index" 767 | checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" 768 | 769 | [[package]] 770 | name = "version_check" 771 | version = "0.9.5" 772 | source = "registry+https://github.com/rust-lang/crates.io-index" 773 | checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" 774 | 775 | [[package]] 776 | name = "wasm-bindgen" 777 | version = "0.2.106" 778 | source = "registry+https://github.com/rust-lang/crates.io-index" 779 | checksum = "0d759f433fa64a2d763d1340820e46e111a7a5ab75f993d1852d70b03dbb80fd" 780 | dependencies = [ 781 | "cfg-if", 782 | "once_cell", 783 | "rustversion", 784 | "wasm-bindgen-macro", 785 | "wasm-bindgen-shared", 786 | ] 787 | 788 | [[package]] 789 | name = "wasm-bindgen-macro" 790 | version = "0.2.106" 791 | source = "registry+https://github.com/rust-lang/crates.io-index" 792 | checksum = "48cb0d2638f8baedbc542ed444afc0644a29166f1595371af4fecf8ce1e7eeb3" 793 | dependencies = [ 794 | "quote", 795 | "wasm-bindgen-macro-support", 796 | ] 797 | 798 | [[package]] 799 | name = "wasm-bindgen-macro-support" 800 | version = "0.2.106" 801 | source = "registry+https://github.com/rust-lang/crates.io-index" 802 | checksum = "cefb59d5cd5f92d9dcf80e4683949f15ca4b511f4ac0a6e14d4e1ac60c6ecd40" 803 | dependencies = [ 804 | "bumpalo", 805 | "proc-macro2", 806 | "quote", 807 | "syn", 808 | "wasm-bindgen-shared", 809 | ] 810 | 811 | [[package]] 812 | name = "wasm-bindgen-shared" 813 | version = "0.2.106" 814 | source = "registry+https://github.com/rust-lang/crates.io-index" 815 | checksum = "cbc538057e648b67f72a982e708d485b2efa771e1ac05fec311f9f63e5800db4" 816 | dependencies = [ 817 | "unicode-ident", 818 | ] 819 | 820 | [[package]] 821 | name = "windows-sys" 822 | version = "0.48.0" 823 | source = "registry+https://github.com/rust-lang/crates.io-index" 824 | checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" 825 | dependencies = [ 826 | "windows-targets", 827 | ] 828 | 829 | [[package]] 830 | name = "windows-targets" 831 | version = "0.48.5" 832 | source = "registry+https://github.com/rust-lang/crates.io-index" 833 | checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" 834 | dependencies = [ 835 | "windows_aarch64_gnullvm", 836 | "windows_aarch64_msvc", 837 | "windows_i686_gnu", 838 | "windows_i686_msvc", 839 | "windows_x86_64_gnu", 840 | "windows_x86_64_gnullvm", 841 | "windows_x86_64_msvc", 842 | ] 843 | 844 | [[package]] 845 | name = "windows_aarch64_gnullvm" 846 | version = "0.48.5" 847 | source = "registry+https://github.com/rust-lang/crates.io-index" 848 | checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" 849 | 850 | [[package]] 851 | name = "windows_aarch64_msvc" 852 | version = "0.48.5" 853 | source = "registry+https://github.com/rust-lang/crates.io-index" 854 | checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" 855 | 856 | [[package]] 857 | name = "windows_i686_gnu" 858 | version = "0.48.5" 859 | source = "registry+https://github.com/rust-lang/crates.io-index" 860 | checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" 861 | 862 | [[package]] 863 | name = "windows_i686_msvc" 864 | version = "0.48.5" 865 | source = "registry+https://github.com/rust-lang/crates.io-index" 866 | checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" 867 | 868 | [[package]] 869 | name = "windows_x86_64_gnu" 870 | version = "0.48.5" 871 | source = "registry+https://github.com/rust-lang/crates.io-index" 872 | checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" 873 | 874 | [[package]] 875 | name = "windows_x86_64_gnullvm" 876 | version = "0.48.5" 877 | source = "registry+https://github.com/rust-lang/crates.io-index" 878 | checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" 879 | 880 | [[package]] 881 | name = "windows_x86_64_msvc" 882 | version = "0.48.5" 883 | source = "registry+https://github.com/rust-lang/crates.io-index" 884 | checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" 885 | --------------------------------------------------------------------------------