#![forbid(unsafe_code)]
use serde::{Deserialize, Serialize};
#[doc = "Convert to API query string (e.g., `2010:2020`)."]
#[doc = "Fields: `page`, `pages`, `per_page`, `total`."]
#[doc = "Prefer using `DataPoint` in downstream code."]
#[doc = "Derives `Eq`, `Ord` so it can be used as a `BTreeMap` key."]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum DateSpec {
Year(i32),
Range { start: i32, end: i32 },
}
impl DateSpec {
pub fn to_query_param(&self) -> String {
match *self {
DateSpec::Year(y) => y.to_string(),
DateSpec::Range { start, end } => format!("{}:{}", start, end),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Meta {
pub page: u32,
pub pages: u32,
#[serde(deserialize_with = "de_u32_from_string_or_number")]
pub per_page: u32,
pub total: u32,
}
fn de_u32_from_string_or_number<'de, D>(deserializer: D) -> Result<u32, D::Error>
where
D: serde::Deserializer<'de>,
{
use serde::de::{self, Visitor};
struct U32Visitor;
impl<'de> Visitor<'de> for U32Visitor {
type Value = u32;
fn expecting(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "a string or integer representing a non-negative number")
}
fn visit_u64<E>(self, v: u64) -> Result<Self::Value, E>
where
E: de::Error,
{
Ok(v as u32)
}
fn visit_i64<E>(self, v: i64) -> Result<Self::Value, E>
where
E: de::Error,
{
if v < 0 {
return Err(E::custom("negative value for u32"));
}
Ok(v as u32)
}
fn visit_str<E>(self, s: &str) -> Result<Self::Value, E>
where
E: de::Error,
{
s.parse::<u32>().map_err(E::custom)
}
}
deserializer.deserialize_any(U32Visitor)
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CodeName {
pub id: String,
pub value: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct IndicatorMeta {
pub id: String,
#[serde(alias = "value")]
pub name: String,
pub unit: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Entry {
pub indicator: CodeName,
pub country: CodeName,
pub countryiso3code: String,
pub date: String,
pub value: Option<f64>,
pub unit: Option<String>,
#[serde(rename = "obs_status")]
pub obs_status: Option<String>,
pub decimal: Option<i32>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct DataPoint {
pub indicator_id: String,
pub indicator_name: String,
pub country_id: String, pub country_name: String,
pub country_iso3: String,
pub year: i32,
pub value: Option<f64>,
pub unit: Option<String>,
pub obs_status: Option<String>,
pub decimal: Option<i32>,
}
impl From<Entry> for DataPoint {
fn from(e: Entry) -> Self {
let year = e.date.parse::<i32>().unwrap_or(0);
Self {
indicator_id: e.indicator.id,
indicator_name: e.indicator.value,
country_id: e.country.id,
country_name: e.country.value,
country_iso3: e.countryiso3code,
year,
value: e.value,
unit: e.unit,
obs_status: e.obs_status,
decimal: e.decimal,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct GroupKey {
pub indicator_id: String,
pub country_iso3: String,
}