Skip to main content

everymap_core/domains/
tiling.rs

1use crate::error::EveryMapResult;
2use async_trait::async_trait;
3use serde::{Deserialize, Serialize};
4
5/// Options for map tile retrieval.
6#[derive(Debug, Clone, Default, Serialize, Deserialize)]
7pub struct TileOptions {
8    /// Response format (e.g., "protobuf", "json")
9    pub format: Option<String>,
10    /// Provider-specific options (HERE: layer, political_view; Mapbox: style_id, access_token)
11    #[serde(default, skip_serializing_if = "Option::is_none")]
12    pub provider_extra: Option<serde_json::Value>,
13}
14
15/// Simplified tile response from the core trait.
16#[derive(Debug, Clone)]
17pub struct TileResponse {
18    pub data: Vec<u8>,
19    pub content_type: Option<String>,
20}
21
22impl TileResponse {
23    /// Create a new tile response from raw bytes.
24    pub fn new(data: Vec<u8>, content_type: Option<String>) -> Self {
25        Self { data, content_type }
26    }
27}
28
29#[async_trait]
30pub trait TileProvider: Send + Sync {
31    async fn get_tile(
32        &self,
33        z: u32,
34        x: u32,
35        y: u32,
36        options: &TileOptions,
37    ) -> EveryMapResult<TileResponse>;
38}
39
40#[cfg(test)]
41mod tests {
42    use super::*;
43
44    #[test]
45    fn test_tile_options_default() {
46        let options = TileOptions::default();
47        assert!(options.format.is_none());
48        assert!(options.provider_extra.is_none());
49    }
50
51    #[test]
52    fn test_tile_options_with_fields() {
53        let options = TileOptions {
54            format: Some("protobuf".to_string()),
55            provider_extra: Some(serde_json::json!({"layer": "base"})),
56        };
57        assert_eq!(options.format, Some("protobuf".to_string()));
58    }
59
60    #[test]
61    fn test_tile_response_construction() {
62        let response = TileResponse::new(vec![1, 2, 3], Some("application/x-protobuf".to_string()));
63        assert_eq!(response.data.len(), 3);
64        assert_eq!(
65            response.content_type,
66            Some("application/x-protobuf".to_string())
67        );
68    }
69
70    // --- TileOptions serde roundtrip ---
71
72    #[test]
73    fn test_tile_options_serde_roundtrip() {
74        let options = TileOptions {
75            format: Some("json".to_string()),
76            provider_extra: Some(serde_json::json!({"layer": "base", "political_view": "ARG"})),
77        };
78        let json = serde_json::to_string(&options).unwrap();
79        let back: TileOptions = serde_json::from_str(&json).unwrap();
80        assert_eq!(back.format, Some("json".to_string()));
81        assert!(back.provider_extra.is_some());
82    }
83
84    // --- TileResponse edge cases ---
85
86    #[test]
87    fn test_tile_response_empty_data() {
88        let response = TileResponse::new(vec![], None);
89        assert!(response.data.is_empty());
90        assert!(response.content_type.is_none());
91    }
92
93    #[test]
94    fn test_tile_response_no_content_type() {
95        let response = TileResponse::new(vec![0x1A, 0x2B, 0x3C, 0x4D], None);
96        assert_eq!(response.data.len(), 4);
97        assert!(response.content_type.is_none());
98    }
99
100    #[test]
101    fn test_tile_response_json_content_type() {
102        let response = TileResponse::new(
103            b"{\"tile\":1}".to_vec(),
104            Some("application/json".to_string()),
105        );
106        assert_eq!(response.content_type, Some("application/json".to_string()));
107    }
108
109    #[test]
110    fn test_tile_response_large_data() {
111        let data: Vec<u8> = (0..8192).map(|i| (i % 256) as u8).collect();
112        let response = TileResponse::new(data, Some("application/x-protobuf".to_string()));
113        assert_eq!(response.data.len(), 8192);
114    }
115
116    // --- TileResponse Clone ---
117
118    #[test]
119    fn test_tile_response_clone() {
120        let response = TileResponse::new(vec![1, 2, 3], Some("test/type".to_string()));
121        let cloned = response.clone();
122        assert_eq!(cloned.data, response.data);
123        assert_eq!(cloned.content_type, response.content_type);
124    }
125
126    // --- TileResponse Debug ---
127
128    #[test]
129    fn test_tile_response_debug() {
130        let response = TileResponse::new(vec![1], Some("application/json".to_string()));
131        let debug_str = format!("{:?}", response);
132        assert!(debug_str.contains("TileResponse"));
133    }
134}