everymap_core/domains/
imaging.rs1use crate::error::EveryMapResult;
2use crate::types::Coordinate;
3use async_trait::async_trait;
4use serde::{Deserialize, Serialize};
5
6#[derive(Debug, Clone, Default, Serialize, Deserialize)]
8pub struct ImageOptions {
9 pub format: Option<String>,
11 pub language: Option<String>,
13 #[serde(default, skip_serializing_if = "Option::is_none")]
15 pub width: Option<u32>,
16 #[serde(default, skip_serializing_if = "Option::is_none")]
18 pub height: Option<u32>,
19 #[serde(default, skip_serializing_if = "Option::is_none")]
21 pub provider_extra: Option<serde_json::Value>,
22}
23
24#[derive(Debug, Clone)]
26pub struct ImageResponse {
27 pub data: Vec<u8>,
28 pub content_type: Option<String>,
29}
30
31impl ImageResponse {
32 pub fn new(data: Vec<u8>, content_type: Option<String>) -> Self {
34 Self { data, content_type }
35 }
36}
37
38#[async_trait]
39pub trait MapImageProvider: Send + Sync {
40 async fn get_image(
41 &self,
42 center: &Coordinate,
43 zoom: u32,
44 size: (u32, u32),
45 options: &ImageOptions,
46 ) -> EveryMapResult<ImageResponse>;
47}
48
49#[cfg(test)]
50mod tests {
51 use super::*;
52
53 #[test]
54 fn test_image_options_default() {
55 let options = ImageOptions::default();
56 assert!(options.format.is_none());
57 assert!(options.language.is_none());
58 assert!(options.width.is_none());
59 assert!(options.height.is_none());
60 assert!(options.provider_extra.is_none());
61 }
62
63 #[test]
64 fn test_image_options_with_fields() {
65 let options = ImageOptions {
66 format: Some("png".to_string()),
67 language: Some("en".to_string()),
68 width: Some(1024),
69 height: Some(768),
70 provider_extra: Some(serde_json::json!({"style": "default"})),
71 };
72 assert_eq!(options.format, Some("png".to_string()));
73 assert_eq!(options.width, Some(1024));
74 assert_eq!(options.height, Some(768));
75 }
76
77 #[test]
78 fn test_image_options_serde_missing_fields() {
79 let back: ImageOptions = serde_json::from_str("{}").unwrap();
80 assert!(back.format.is_none());
81 assert!(back.language.is_none());
82 assert!(back.width.is_none());
83 assert!(back.height.is_none());
84 assert!(back.provider_extra.is_none());
85 }
86
87 #[test]
88 fn test_image_response_construction() {
89 let response =
90 ImageResponse::new(vec![0x89, 0x50, 0x4E, 0x47], Some("image/png".to_string()));
91 assert_eq!(response.data.len(), 4);
92 assert_eq!(response.content_type, Some("image/png".to_string()));
93 }
94
95 #[test]
98 fn test_image_options_serde_roundtrip() {
99 let options = ImageOptions {
100 format: Some("jpg".to_string()),
101 language: Some("ja".to_string()),
102 width: Some(640),
103 height: Some(480),
104 provider_extra: Some(
105 serde_json::json!({"maptype": "satellite", "markers": [{"lat": 35.6762, "lng": 139.6503}]}),
106 ),
107 };
108 let json = serde_json::to_string(&options).unwrap();
109 let back: ImageOptions = serde_json::from_str(&json).unwrap();
110 assert_eq!(back.format, Some("jpg".to_string()));
111 assert_eq!(back.width, Some(640));
112 assert_eq!(back.height, Some(480));
113 assert!(back.provider_extra.is_some());
114 }
115
116 #[test]
119 fn test_image_response_empty_data() {
120 let response = ImageResponse::new(vec![], None);
121 assert!(response.data.is_empty());
122 assert!(response.content_type.is_none());
123 }
124
125 #[test]
126 fn test_image_response_no_content_type() {
127 let response = ImageResponse::new(vec![0xFF, 0xD8, 0xFF], None);
128 assert_eq!(response.data.len(), 3);
129 assert!(response.content_type.is_none());
130 }
131
132 #[test]
133 fn test_image_response_jpeg_content_type() {
134 let response =
135 ImageResponse::new(vec![0xFF, 0xD8, 0xFF, 0xE0], Some("image/jpeg".to_string()));
136 assert_eq!(response.content_type, Some("image/jpeg".to_string()));
137 }
138
139 #[test]
140 fn test_image_response_large_data() {
141 let data: Vec<u8> = (0..1024).map(|i| (i % 256) as u8).collect();
142 let response = ImageResponse::new(data, Some("image/png".to_string()));
143 assert_eq!(response.data.len(), 1024);
144 }
145
146 #[test]
149 fn test_image_response_clone() {
150 let response = ImageResponse::new(vec![1, 2, 3], Some("image/png".to_string()));
151 let cloned = response.clone();
152 assert_eq!(cloned.data, response.data);
153 assert_eq!(cloned.content_type, response.content_type);
154 }
155}