monocle/server/handlers/
country.rs

1//! Country handlers for country lookup operations
2//!
3//! This module provides handlers for country-related methods like `country.lookup`.
4
5use crate::lens::country::{CountryEntry, CountryLens, CountryLookupArgs};
6use crate::server::handler::{WsContext, WsError, WsMethod, WsRequest, WsResult};
7use crate::server::op_sink::WsOpSink;
8use async_trait::async_trait;
9use serde::{Deserialize, Serialize};
10use std::sync::Arc;
11
12// =============================================================================
13// country.lookup
14// =============================================================================
15
16/// Parameters for country.lookup
17#[derive(Debug, Clone, Default, Deserialize, Serialize)]
18pub struct CountryLookupParams {
19    /// Search query: country code (e.g., "US") or partial name (e.g., "united")
20    #[serde(default)]
21    pub query: Option<String>,
22
23    /// List all countries
24    #[serde(default)]
25    pub all: Option<bool>,
26}
27
28/// Response for country.lookup
29#[derive(Debug, Clone, Serialize)]
30pub struct CountryLookupResponse {
31    /// Matching countries
32    pub countries: Vec<CountryEntry>,
33}
34
35/// Handler for country.lookup method
36pub struct CountryLookupHandler;
37
38#[async_trait]
39impl WsMethod for CountryLookupHandler {
40    const METHOD: &'static str = "country.lookup";
41    const IS_STREAMING: bool = false;
42
43    type Params = CountryLookupParams;
44
45    fn validate(params: &Self::Params) -> WsResult<()> {
46        // Must have either a query or all=true
47        if params.query.is_none() && !params.all.unwrap_or(false) {
48            return Err(WsError::invalid_params(
49                "Either 'query' or 'all: true' is required",
50            ));
51        }
52        Ok(())
53    }
54
55    async fn handle(
56        _ctx: Arc<WsContext>,
57        _req: WsRequest,
58        params: Self::Params,
59        sink: WsOpSink,
60    ) -> WsResult<()> {
61        // Create the country lens
62        let lens = CountryLens::new();
63
64        // Create args from params
65        let args = if params.all.unwrap_or(false) {
66            CountryLookupArgs::all_countries()
67        } else {
68            CountryLookupArgs::new(params.query.unwrap_or_default())
69        };
70
71        // Perform the search
72        let countries = lens
73            .search(&args)
74            .map_err(|e| WsError::operation_failed(e.to_string()))?;
75
76        // Send response
77        let response = CountryLookupResponse { countries };
78        sink.send_result(response)
79            .await
80            .map_err(|e| WsError::internal(e.to_string()))?;
81
82        Ok(())
83    }
84}
85
86// =============================================================================
87// Tests
88// =============================================================================
89
90#[cfg(test)]
91mod tests {
92    use super::*;
93
94    #[test]
95    fn test_country_lookup_params_default() {
96        let params = CountryLookupParams::default();
97        assert!(params.query.is_none());
98        assert!(params.all.is_none());
99    }
100
101    #[test]
102    fn test_country_lookup_params_deserialization() {
103        let json = r#"{"query": "US"}"#;
104        let params: CountryLookupParams = serde_json::from_str(json).unwrap();
105        assert_eq!(params.query, Some("US".to_string()));
106        assert!(params.all.is_none());
107
108        let json = r#"{"all": true}"#;
109        let params: CountryLookupParams = serde_json::from_str(json).unwrap();
110        assert!(params.query.is_none());
111        assert_eq!(params.all, Some(true));
112    }
113
114    #[test]
115    fn test_country_lookup_params_validation() {
116        // Empty params should fail
117        let params = CountryLookupParams::default();
118        assert!(CountryLookupHandler::validate(&params).is_err());
119
120        // With query should pass
121        let params = CountryLookupParams {
122            query: Some("US".to_string()),
123            all: None,
124        };
125        assert!(CountryLookupHandler::validate(&params).is_ok());
126
127        // With all=true should pass
128        let params = CountryLookupParams {
129            query: None,
130            all: Some(true),
131        };
132        assert!(CountryLookupHandler::validate(&params).is_ok());
133    }
134
135    #[test]
136    fn test_country_lookup_response_serialization() {
137        let response = CountryLookupResponse {
138            countries: vec![CountryEntry {
139                code: "US".to_string(),
140                name: "United States".to_string(),
141            }],
142        };
143        let json = serde_json::to_string(&response).unwrap();
144        assert!(json.contains("\"US\""));
145        assert!(json.contains("United States"));
146    }
147
148    #[test]
149    fn test_country_lens_lookup() {
150        let lens = CountryLens::new();
151        let results = lens.lookup("US");
152        assert_eq!(results.len(), 1);
153        assert_eq!(results[0].code, "US");
154    }
155}