gwelle 0.1.0

Lightweight Rust client for the Google Trends API
Documentation
use crate::client::{format_req_param, TrendsClient};
use crate::models::{Coordinates, GeoPoint, GeoResolution, InterestByRegion, WidgetToken};
use crate::{Result, TrendsError};

impl TrendsClient {
    pub async fn interest_by_region(
        &self,
        token: &WidgetToken,
        resolution: GeoResolution,
    ) -> Result<InterestByRegion> {
        let url = "https://trends.google.com/trends/api/widgetdata/comparedgeo";

        let mut request_obj = token.request.clone();
        if let Some(obj) = request_obj.as_object_mut() {
            obj.insert(
                "resolution".to_string(),
                serde_json::json!(resolution.as_str()),
            );
        }

        let req_str = format_req_param(&request_obj)?;

        let tz_str = self.tz.to_string();

        let params = vec![
            ("hl", self.hl.as_str()),
            ("tz", tz_str.as_str()),
            ("req", req_str.as_str()),
            ("token", token.token.as_str()),
        ];

        let result = self.get_json_with_params(url, &params).await?;

        let default_obj = result.get("default").ok_or_else(|| {
            TrendsError::TokenNotFound("Missing 'default' in comparedgeo response".into())
        })?;

        let mut geo_map = Vec::new();
        if let Some(geo_arr) = default_obj.get("geoMapData").and_then(|t| t.as_array()) {
            for item in geo_arr {
                let geo_code = item
                    .get("geoCode")
                    .and_then(|v| v.as_str())
                    .unwrap_or("")
                    .to_string();
                let geo_name = item
                    .get("geoName")
                    .and_then(|v| v.as_str())
                    .unwrap_or("")
                    .to_string();

                let mut values = Vec::new();
                if let Some(val_arr) = item.get("value").and_then(|v| v.as_array()) {
                    for v in val_arr {
                        let raw = v.as_u64().ok_or_else(|| {
                            TrendsError::TokenNotFound(
                                "Non-numeric entry in geoMapData.value".into(),
                            )
                        })?;
                        let parsed = u32::try_from(raw).map_err(|_| {
                            TrendsError::TokenNotFound(
                                "Out-of-range entry in geoMapData.value".into(),
                            )
                        })?;
                        values.push(parsed);
                    }
                }

                let coordinates = if let Some(coords) = item.get("coordinates") {
                    let lat = coords.get("lat").and_then(|v| v.as_f64()).unwrap_or(0.0);
                    let lng = coords.get("lng").and_then(|v| v.as_f64()).unwrap_or(0.0);
                    Some(Coordinates { lat, lng })
                } else {
                    None
                };

                geo_map.push(GeoPoint {
                    geo_code,
                    geo_name,
                    values,
                    coordinates,
                });
            }
        }

        Ok(InterestByRegion {
            geo_map_data: geo_map,
        })
    }
}