geospatial_types/
vector.rs1use serde::{Deserialize, Serialize};
10
11use crate::error::SDKError;
12
13const GZIP_MAGIC_FIRST_BYTE: u8 = 0x1f;
15const GZIP_MAGIC_SECOND_BYTE: u8 = 0x8b;
17
18#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
24#[serde(rename_all = "snake_case")]
25pub enum VectorTileEncoding {
26 MapboxVectorTile,
28 GzippedMapboxVectorTile,
30}
31
32#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
34pub struct VectorTileData {
35 pub tile_x: u32,
37 pub tile_y: u32,
39 pub zoom_level: u8,
41 pub tile_encoding: VectorTileEncoding,
43 pub tile_bytes: Vec<u8>,
45}
46
47impl VectorTileData {
48 pub fn from_wire_bytes(
52 tile_x: u32,
53 tile_y: u32,
54 zoom_level: u8,
55 wire_bytes: Vec<u8>,
56 ) -> Result<Self, SDKError> {
57 let tile_encoding = Self::sniff_tile_encoding(&wire_bytes)?;
58 Ok(Self {
59 tile_x,
60 tile_y,
61 zoom_level,
62 tile_encoding,
63 tile_bytes: wire_bytes,
64 })
65 }
66
67 fn sniff_tile_encoding(wire_bytes: &[u8]) -> Result<VectorTileEncoding, SDKError> {
70 if wire_bytes.is_empty() {
71 return Err(SDKError::ResponseParsingFailure(
72 "vector tile payload is empty".to_string(),
73 ));
74 }
75 let carries_gzip_magic = wire_bytes[0] == GZIP_MAGIC_FIRST_BYTE
76 && wire_bytes.len() > 1
77 && wire_bytes[1] == GZIP_MAGIC_SECOND_BYTE;
78 if carries_gzip_magic {
79 Ok(VectorTileEncoding::GzippedMapboxVectorTile)
80 } else {
81 Ok(VectorTileEncoding::MapboxVectorTile)
82 }
83 }
84}
85
86#[cfg(test)]
87mod tests {
88 use super::{VectorTileData, VectorTileEncoding};
89 use crate::SDKError;
90
91 const GZIP_MAGIC_BYTES: [u8; 2] = [0x1f, 0x8b];
93 const LAYER_FIELD_PROTOBUF_KEY: u8 = 0x1a;
95
96 #[test]
97 fn plain_protobuf_bytes_classify_as_mapbox_vector_tiles() {
98 let tile =
99 VectorTileData::from_wire_bytes(1, 2, 3, vec![LAYER_FIELD_PROTOBUF_KEY, 0]).unwrap();
100 assert_eq!(tile.tile_encoding, VectorTileEncoding::MapboxVectorTile);
101 assert_eq!(tile.tile_bytes, vec![LAYER_FIELD_PROTOBUF_KEY, 0]);
102 }
103
104 #[test]
105 fn gzip_magic_bytes_classify_as_gzipped_vector_tiles() {
106 let wire_bytes = [GZIP_MAGIC_BYTES.as_slice(), &[0x08]].concat();
107 let tile = VectorTileData::from_wire_bytes(4, 5, 6, wire_bytes).unwrap();
108 assert_eq!(
109 tile.tile_encoding,
110 VectorTileEncoding::GzippedMapboxVectorTile
111 );
112 }
113
114 #[test]
115 fn empty_wire_payload_is_a_parsing_failure() {
116 assert!(matches!(
117 VectorTileData::from_wire_bytes(0, 0, 0, Vec::new()),
118 Err(SDKError::ResponseParsingFailure(_))
119 ));
120 }
121}