abs_data/builders/
datakey_builder.rs

1use 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}