popgetter_core/
data_request_spec.rs1use itertools::Itertools;
2use nonempty::nonempty;
3use serde::{Deserialize, Serialize};
4
5use crate::geo::BBox;
6use crate::search::{
7 CaseSensitivity, DownloadParams, GeometryLevel, MatchType, MetricId, Params, SearchConfig,
8 SearchContext, SearchParams, SearchText, YearRange,
9};
10
11#[derive(Serialize, Deserialize, Clone, Debug, Default)]
12pub struct DataRequestSpec {
13 pub geometry: Option<GeometrySpec>,
14 pub region: Vec<RegionSpec>,
15 pub metrics: Vec<MetricSpec>,
16 pub years: Option<Vec<String>>,
17}
18
19impl TryFrom<DataRequestSpec> for Params {
22 type Error = anyhow::Error;
23 fn try_from(value: DataRequestSpec) -> Result<Self, Self::Error> {
24 Ok(Self {
26 search: SearchParams {
27 text: value
29 .metrics
30 .iter()
31 .filter_map(|metric| match metric {
32 MetricSpec::MetricText(text) => Some(SearchText {
33 text: text.clone(),
34 context: nonempty![
35 SearchContext::HumanReadableName,
36 SearchContext::Hxl,
37 SearchContext::Description
38 ],
39 config: SearchConfig {
40 match_type: MatchType::Regex,
41 case_sensitivity: CaseSensitivity::Insensitive,
42 },
43 }),
44 _ => None,
45 })
46 .collect_vec(),
47 year_range: if let Some(v) = value.years {
48 Some(
49 v.iter()
50 .map(|year| year.parse::<YearRange>())
51 .collect::<Result<Vec<_>, anyhow::Error>>()?,
52 )
53 } else {
54 None
55 },
56 metric_id: value
57 .metrics
58 .iter()
59 .filter_map(|metric| match metric {
60 MetricSpec::MetricId(m) => Some(m.clone()),
61 _ => None,
62 })
63 .collect_vec(),
64 geometry_level: value.geometry.as_ref().and_then(|geometry| {
65 geometry
66 .geometry_level
67 .to_owned()
68 .map(|geometry_level| GeometryLevel {
69 value: geometry_level,
70 config: SearchConfig {
71 match_type: MatchType::Exact,
72 case_sensitivity: CaseSensitivity::Insensitive,
73 },
74 })
75 }),
76 source_data_release: None,
77 source_download_url: None,
78 data_publisher: None,
79 country: None,
80 source_metric_id: None,
81 region_spec: value.region.clone(),
82 },
83 download: DownloadParams {
84 include_geoms: value.geometry.unwrap_or_default().include_geoms,
85 region_spec: value.region,
86 },
87 })
88 }
89}
90
91#[derive(Clone, Serialize, Deserialize, Debug)]
92pub enum MetricSpec {
93 MetricId(MetricId),
94 MetricText(String),
95 DataProduct(String),
96}
97
98#[derive(Serialize, Deserialize, Debug, Clone)]
99pub struct GeometrySpec {
100 pub geometry_level: Option<String>,
101 pub include_geoms: bool,
102}
103
104impl Default for GeometrySpec {
105 fn default() -> Self {
106 Self {
107 include_geoms: true,
108 geometry_level: None,
109 }
110 }
111}
112
113#[derive(Clone, Serialize, Deserialize, Debug)]
114pub enum RegionSpec {
115 BoundingBox(BBox),
116 Polygon(Polygon),
117 NamedArea(String),
118}
119
120impl RegionSpec {
121 pub fn bbox(&self) -> Option<BBox> {
122 match self {
123 RegionSpec::BoundingBox(bbox) => Some(bbox.clone()),
124 _ => None,
125 }
126 }
127}
128
129#[derive(Clone, Serialize, Deserialize, Debug)]
130pub struct Polygon;
131
132#[cfg(test)]
133mod tests {}