1use std::{fmt::Display, ops::Add};
2use crate::error::Error;
3
4
5#[derive(Clone, Copy, Debug, Default)]
6pub enum Protocol {
11 HTTP,
12 #[default]
13 HTTPS,
14}
15
16impl Display for Protocol {
17 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
18 match self {
19 Self::HTTP => write!(f, "http"),
20 Self::HTTPS => write!(f, "https"),
21 }
22 }
23}
24
25
26#[derive(Clone, Copy, Debug, Default)]
27pub enum WSEntryPoint {
31 #[default]
32 Main,
34}
35
36impl Display for WSEntryPoint {
37 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
38 match self {
39 Self::Main => write!(f, "data-api.ecb.europa.eu/service"),
40 }
41 }
42}
43
44
45#[derive(Clone, Copy, Debug, Default, PartialEq)]
46pub enum Resource {
51 #[default]
52 Data,
53 Schema,
54 MetadataDataStructure,
55 MetadataMetadataStructure,
56 MetadataCategoryScheme,
57 MetadataConceptScheme,
58 MetadataCodeList,
59 MetadataHierarchicalCodeList,
60 MetadataOrganisationsScheme,
61 MetadataAgencyScheme,
62 MetadataDataProvidersScheme,
63 MetadataDataConsumerScheme,
64 MetadataOrganisationUnitScheme,
65 MetadataDataFlow,
66 MetadataMetadataFlow,
67 MetadataReportingTaxonomy,
68 MetadataProvisionAgreement,
69 MetadataStructureSet,
70 MetadataProcess,
71 MetadataCategorisation,
72 MetadataContentConstraint,
73 MetadataAttachmentConstraint,
74 MetadataStructure,
75}
76
77impl Resource {
78 pub fn all_data_resources() -> Vec<Self> {
80 vec![Self::Data]
81 }
82
83 pub fn all_schema_resources() -> Vec<Self> {
85 vec![Self::Schema]
86 }
87
88 pub fn all_metadata_resources() -> Vec<Self> {
90 vec![
91 Self::MetadataDataStructure,
92 Self::MetadataMetadataStructure,
93 Self::MetadataCategoryScheme,
94 Self::MetadataConceptScheme,
95 Self::MetadataCodeList,
96 Self::MetadataHierarchicalCodeList,
97 Self::MetadataOrganisationsScheme,
98 Self::MetadataAgencyScheme,
99 Self::MetadataDataProvidersScheme,
100 Self::MetadataDataConsumerScheme,
101 Self::MetadataOrganisationUnitScheme,
102 Self::MetadataDataFlow,
103 Self::MetadataMetadataFlow,
104 Self::MetadataReportingTaxonomy,
105 Self::MetadataProvisionAgreement,
106 Self::MetadataStructureSet,
107 Self::MetadataProcess,
108 Self::MetadataCategorisation,
109 Self::MetadataContentConstraint,
110 Self::MetadataAttachmentConstraint,
111 Self::MetadataStructure,
112 ]
113 }
114}
115
116impl Display for Resource {
117 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
118 match self {
119 Self::Data => write!(f, "data"),
120 Self::Schema => write!(f, "schema"),
121 Self::MetadataDataStructure => write!(f, "datastructure"),
122 Self::MetadataMetadataStructure => write!(f, "metadatastructure"),
123 Self::MetadataCategoryScheme => write!(f, "categoryscheme"),
124 Self::MetadataConceptScheme => write!(f, "Conceptscheme"), Self::MetadataCodeList => write!(f, "codelist"),
126 Self::MetadataHierarchicalCodeList => write!(f, "hierarchicalcodelist"),
127 Self::MetadataOrganisationsScheme => write!(f, "organisationsscheme"),
128 Self::MetadataAgencyScheme => write!(f, "agencyscheme"),
129 Self::MetadataDataProvidersScheme => write!(f, "dataprovidersscheme"),
130 Self::MetadataDataConsumerScheme => write!(f, "dataconsumerscheme"),
131 Self::MetadataOrganisationUnitScheme => write!(f, "organisationunitscheme"),
132 Self::MetadataDataFlow => write!(f, "dataflow"),
133 Self::MetadataMetadataFlow => write!(f, "metadataflow"),
134 Self::MetadataReportingTaxonomy => write!(f, "reportingtaxonomy"),
135 Self::MetadataProvisionAgreement => write!(f, "provisionagreement"),
136 Self::MetadataStructureSet => write!(f, "structureset"),
137 Self::MetadataProcess => write!(f, "process"),
138 Self::MetadataCategorisation => write!(f, "categorisation"),
139 Self::MetadataContentConstraint => write!(f, "contentconstraint"),
140 Self::MetadataAttachmentConstraint => write!(f, "attachmentconstraint"),
141 Self::MetadataStructure => write!(f, "structure"),
142 }
143 }
144}
145
146
147#[derive(Clone, Debug, Default)]
148pub struct FlowRef {
165 pub agency_id: Option<String>,
167 pub flow_id: String,
173 pub version: Option<String>,
175}
176
177impl Display for FlowRef {
178 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
179 let agency_id: String = self.agency_id.as_ref().map_or("all".to_owned(), |v| v.clone() );
180 let flow_id: String = self.flow_id.clone();
181 let version: String = self.version.as_ref().map_or("latest".to_owned(), |v| v.clone() );
182 write!(f, "{agency_id},{flow_id},{version}")
183 }
184}
185
186
187#[derive(Clone, Copy, Debug, Default)]
188pub enum Context {
190 #[default]
191 DataStructure,
193 DataFlow,
195 ProvisionAgreement,
198}
199
200impl Display for Context {
201 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
202 match self {
203 Self::DataStructure => write!(f, "datastructure"),
204 Self::DataFlow => write!(f, "dataflow"),
205 Self::ProvisionAgreement => write!(f, "provisionagreement"),
206 }
207 }
208}
209
210
211#[derive(Clone, Debug, Default)]
212pub struct Query {
262 pub protocol: Protocol,
264 pub ws_entry_point: WSEntryPoint,
265 pub resource: Resource,
266 pub agency_id: Option<String>,
269 pub resource_id: Option<String>,
271 pub version: Option<String>,
273 pub flow_ref: Option<FlowRef>,
275 pub series_key: Option<String>,
281 pub context: Option<Context>,
283}
284
285impl Query {
286 pub fn new() -> Self {
287 Self::default()
288 }
289
290 pub fn protocol(mut self, protocol: Protocol) -> Self {
291 self.protocol = protocol;
292 self
293 }
294
295 pub fn ws_entry_point(mut self, ws_entry_point: WSEntryPoint) -> Self {
296 self.ws_entry_point = ws_entry_point;
297 self
298 }
299
300 pub fn resource(mut self, resource: Resource) -> Self {
301 self.resource = resource;
302 self
303 }
304
305 pub fn flow_ref(mut self, flow_ref: FlowRef) -> Self {
306 self.flow_ref = Some(flow_ref);
307 self
308 }
309
310 pub fn series_key(mut self, series_key: &str) -> Self {
311 self.series_key = Some(series_key.to_owned());
312 self
313 }
314
315 pub fn context(mut self, context: Context) -> Self {
316 self.context = Some(context);
317 self
318 }
319
320 pub fn agency_id(mut self, agency_id: &str) -> Self {
321 self.agency_id = Some(agency_id.to_owned());
322 self
323 }
324
325 pub fn resource_id(mut self, resource_id: &str) -> Self {
326 self.resource_id = Some(resource_id.to_owned());
327 self
328 }
329
330 pub fn version(mut self, version: &str) -> Self {
331 self.version = Some(version.to_owned());
332 self
333 }
334
335 pub fn validate_query(&self, permitted_resources: Vec<Resource>) -> Result<(), Error> {
336 if !permitted_resources.contains(&self.resource) {
337 return Err(Error::WrongResourceRequested);
338 }
339 Ok(())
340 }
341
342 pub fn generate_url(&self) -> Result<String, Error> {
346 let mut query: String = format!("{}://{}/{}", self.protocol.to_string(), self.ws_entry_point.to_string(), self.resource.to_string());
348 query = match self.resource {
349 Resource::Data => {
351 query
352 .add("/").add(&self.flow_ref.as_ref().ok_or(Error::MissingQueryAttribute { attribute: "reference to dataflow".to_owned(), })?.to_string())
353 .add("/").add(&self.series_key.as_ref().ok_or(Error::MissingQueryAttribute { attribute: "series key".to_owned(), })?)
354 },
355 Resource::Schema => {
357 query
358 .add("/").add(&self.context.as_ref().ok_or(Error::MissingQueryAttribute { attribute: "schema context".to_owned() })?.to_string())
359 .add("/").add(&self.agency_id.as_ref().ok_or(Error::MissingQueryAttribute { attribute: "agency ID".to_owned() })?)
360 .add("/").add(&self.resource_id.as_ref().ok_or(Error::MissingQueryAttribute { attribute: "resource ID".to_owned() })?)
361 .add("/").add(&self.version.as_ref().ok_or(Error::MissingQueryAttribute { attribute: "version number".to_owned() })?)
362 },
363 _ => {
365 query
366 .add("/").add(&self.agency_id.as_ref().ok_or(Error::MissingQueryAttribute { attribute: "agency ID".to_owned() })?)
367 .add("/").add(&self.resource_id.as_ref().ok_or(Error::MissingQueryAttribute { attribute: "resource ID".to_owned() })?)
368 .add("/").add(&self.version.as_ref().ok_or(Error::MissingQueryAttribute { attribute: "version number".to_owned() })?)
369 },
370 };
371 Ok(query)
372 }
373}
374
375
376#[cfg(test)]
377mod tests {
378 use crate::query::{Resource, FlowRef, Context, Query};
379
380 #[test]
381 fn unit_test_generate_url() -> () {
382 let query: Query = Query::new()
384 .flow_ref(FlowRef { agency_id: None, flow_id: String::from("EXR"), version: None, })
385 .series_key("M.USD.EUR.SP00.A");
386 assert_eq!(query.generate_url().unwrap(), "https://data-api.ecb.europa.eu/service/data/all,EXR,latest/M.USD.EUR.SP00.A".to_owned());
387 let query: Query = Query::new()
389 .resource(Resource::Schema)
390 .context(Context::DataStructure)
391 .agency_id("ECB")
392 .resource_id("ECB_EXR1")
393 .version("1.0");
394 assert_eq!(query.generate_url().unwrap(), "https://data-api.ecb.europa.eu/service/schema/datastructure/ECB/ECB_EXR1/1.0".to_owned());
395 let query: Query = Query::new()
397 .resource(Resource::MetadataCodeList)
398 .agency_id("all")
399 .resource_id("all")
400 .version("latest");
401 assert_eq!(query.generate_url().unwrap(), "https://data-api.ecb.europa.eu/service/codelist/all/all/latest".to_owned());
402 }
403}