abs_data/builders/
datakey_builder.rs1use std::collections::{HashMap, HashSet};
2
3use crate::{
4 error_code::ErrorCode,
5 models::typed::{
6 dataflow_identifier::DataflowIdentifier, datakey::DataKey,
7 datakey_dimension::DataKeyDimension, reference::Reference, structure_type::StructureType,
8 },
9 result::Result,
10};
11
12use super::sdmx_meta_request_builder::SdmxMetaRequestBuilder;
13
14pub struct DataKeyBuilder<'a> {
15 dataflow_identifier: &'a DataflowIdentifier,
16 dimensions: HashMap<Box<str>, HashSet<Box<str>>>,
17}
18
19impl<'a> DataKeyBuilder<'a> {
20 async fn get_constraints(
21 id: &DataflowIdentifier,
22 ) -> Result<(HashMap<Box<str>, u8>, HashMap<Box<str>, HashSet<Box<str>>>)> {
23 let mut builder = SdmxMetaRequestBuilder::new(&StructureType::DataFlow)
24 .structure_id(id.structure_id())
25 .reference(&Reference::StructureType(StructureType::ContentConstraint));
26
27 if let Some(agency_id) = id.agency_id() {
28 builder = builder.agency_id(agency_id)
29 }
30 if let Some(version) = id.version() {
31 builder = builder.structure_version(version)
32 }
33
34 let data = builder.build().send().await?.data;
35
36 let key_values = data
37 .content_constraints
38 .as_ref()
39 .ok_or(ErrorCode::MissingExpectedOptionalField(
40 "content constraints".into(),
41 ))?
42 .first()
43 .ok_or(ErrorCode::MissingExpectedValueOnField(
44 "content constraint".into(),
45 ))?
46 .cube_regions
47 .as_ref()
48 .ok_or(ErrorCode::MissingExpectedOptionalField(
49 "cube regions".into(),
50 ))?
51 .first()
52 .ok_or(ErrorCode::MissingExpectedValueOnField("cub regions".into()))?
53 .key_values
54 .as_ref();
55
56 let mut key_order = HashMap::new();
57 let mut constraints = HashMap::new();
58
59 for (i, kv) in key_values.iter().enumerate() {
60 let index = i as u8;
61 let id_clone = kv.id.clone();
62 let values_set: HashSet<Box<str>> = kv.values.clone().into_vec().into_iter().collect();
63
64 key_order.insert(id_clone.clone(), index);
65 constraints.insert(id_clone, values_set);
66 }
67
68 Ok((key_order, constraints))
69 }
70
71 fn dimensions_not_in_constraints(
72 dimensions: &HashMap<Box<str>, HashSet<Box<str>>>,
73 constraints: &HashMap<Box<str>, HashSet<Box<str>>>,
74 ) -> Vec<Box<str>> {
75 dimensions
76 .iter()
77 .filter_map(|(key, value_set)| {
78 if let Some(constraint_set) = constraints.get(key) {
79 if !value_set.is_subset(constraint_set) {
80 return Some(key.clone());
81 }
82 } else {
83 return Some(key.clone());
84 }
85 None
86 })
87 .collect()
88 }
89
90 pub fn new(dataflow_identifier: &'a DataflowIdentifier) -> Self {
91 Self {
92 dataflow_identifier,
93 dimensions: HashMap::new(),
94 }
95 }
96
97 pub fn add(mut self, dimension: &'a DataKeyDimension) -> Self {
98 self.dimensions
99 .entry(dimension.key().into())
100 .or_insert_with(HashSet::new)
101 .insert(dimension.value().into());
102
103 self
104 }
105
106 pub async fn build(self) -> Result<DataKey> {
107 let (key_order, constraints) = Self::get_constraints(self.dataflow_identifier).await?;
108
109 let errors = Self::dimensions_not_in_constraints(&self.dimensions, &constraints);
110 if !errors.is_empty() {
111 return Err(ErrorCode::DataKeyContainsInvalidDimensions(
112 format!("{:?}", errors).into(),
113 ));
114 }
115
116 let mut key = self
117 .dimensions
118 .into_iter()
119 .map(|(m, s)| (m, s.into_iter().collect::<Vec<_>>().join("+")))
120 .collect::<Vec<_>>();
121
122 key.sort_by_key(|(m, _)| key_order.get(m));
123
124 let key_string = key
125 .iter()
126 .map(|(_, s)| s.to_owned())
127 .collect::<Vec<_>>()
128 .join(".");
129
130 Ok(DataKey::parse(&key_string).expect("should always be valid"))
131 }
132}