Skip to main content

everymap_core/domains/
attributes.rs

1use crate::error::EveryMapResult;
2use async_trait::async_trait;
3use serde::{Deserialize, Serialize};
4
5/// Options for map attribute queries.
6#[derive(Debug, Clone, Default, Serialize, Deserialize)]
7pub struct AttributeOptions {
8    /// Bounding box for the attribute query (format: "south,west,north,east")
9    pub bbox: Option<String>,
10    /// Preferred response language (BCP 47 language tag)
11    pub language: Option<String>,
12    /// Provider-specific options (HERE: layer, format, ids, srs, include, exclude)
13    #[serde(default, skip_serializing_if = "Option::is_none")]
14    pub provider_extra: Option<serde_json::Value>,
15}
16
17/// Simplified attribute response from the core trait.
18#[derive(Debug, Clone, Serialize, Deserialize)]
19pub struct AttributeResponse {
20    /// The attribute data as JSON
21    pub data: serde_json::Value,
22}
23
24#[async_trait]
25pub trait AttributeProvider: Send + Sync {
26    async fn get_attributes(&self, options: &AttributeOptions)
27        -> EveryMapResult<AttributeResponse>;
28}
29
30#[cfg(test)]
31mod tests {
32    use super::*;
33
34    #[test]
35    fn test_attribute_options_default() {
36        let options = AttributeOptions::default();
37        assert!(options.bbox.is_none());
38        assert!(options.language.is_none());
39        assert!(options.provider_extra.is_none());
40    }
41
42    #[test]
43    fn test_attribute_options_with_fields() {
44        let options = AttributeOptions {
45            bbox: Some("52.0,13.0,52.5,13.5".to_string()),
46            language: Some("en".to_string()),
47            provider_extra: Some(serde_json::json!({"layer": "speed_limit"})),
48        };
49        assert_eq!(options.bbox, Some("52.0,13.0,52.5,13.5".to_string()));
50        assert_eq!(options.language, Some("en".to_string()));
51    }
52
53    #[test]
54    fn test_attribute_response_construction() {
55        let response = AttributeResponse {
56            data: serde_json::json!({"features": []}),
57        };
58        assert!(response.data.is_object());
59    }
60
61    // --- AttributeResponse serde roundtrip ---
62
63    #[test]
64    fn test_attribute_response_serde_roundtrip() {
65        let response = AttributeResponse {
66            data: serde_json::json!({
67                "features": [
68                    {"id": 1, "speed_limit": 50, "road_name": "Main St"},
69                    {"id": 2, "speed_limit": 30, "road_name": "School Zone"}
70                ],
71                "type": "FeatureCollection"
72            }),
73        };
74        let json = serde_json::to_string(&response).unwrap();
75        let back: AttributeResponse = serde_json::from_str(&json).unwrap();
76        assert!(back.data.is_object());
77        assert_eq!(back.data["features"].as_array().unwrap().len(), 2);
78    }
79
80    #[test]
81    fn test_attribute_response_empty_object() {
82        let response = AttributeResponse {
83            data: serde_json::json!({}),
84        };
85        let json = serde_json::to_string(&response).unwrap();
86        let back: AttributeResponse = serde_json::from_str(&json).unwrap();
87        assert!(back.data.is_object());
88        assert!(back.data.as_object().unwrap().is_empty());
89    }
90
91    // --- AttributeOptions serde roundtrip ---
92
93    #[test]
94    fn test_attribute_options_serde_roundtrip() {
95        let options = AttributeOptions {
96            bbox: Some("52.0,13.0,52.5,13.5".to_string()),
97            language: Some("en".to_string()),
98            provider_extra: Some(serde_json::json!({"layer": "speed_limit", "format": "json"})),
99        };
100        let json = serde_json::to_string(&options).unwrap();
101        let back: AttributeOptions = serde_json::from_str(&json).unwrap();
102        assert_eq!(back.bbox, Some("52.0,13.0,52.5,13.5".to_string()));
103        assert!(back.provider_extra.is_some());
104    }
105
106    // --- Edge cases ---
107
108    #[test]
109    fn test_attribute_response_with_array_data() {
110        let response = AttributeResponse {
111            data: serde_json::json!([1, 2, 3]),
112        };
113        assert!(response.data.is_array());
114    }
115
116    #[test]
117    fn test_attribute_response_with_null_data() {
118        let response = AttributeResponse {
119            data: serde_json::Value::Null,
120        };
121        assert!(response.data.is_null());
122    }
123}