iadb_api/
utils.rs

1use std::{error::Error, fmt};
2use reqwest::{Client, Response};
3use csv::{Reader, ReaderBuilder, StringRecord};
4use crate::{BASE_URL, schemas::{IADBSeries, IADBDataPoint}};
5
6
7pub enum CSVF {
8    TT,
9    TN,
10    CT,
11    CN,
12}
13
14impl fmt::Display for CSVF {
15    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
16        match self {
17            CSVF::TT => write!(f, "TT"),
18            CSVF::TN => write!(f, "TN"),
19            CSVF::CT => write!(f, "CT"),
20            CSVF::CN => write!(f, "CN"),
21        }
22    }
23}
24
25
26pub enum VPD {
27    Y,
28    N,
29}
30
31impl fmt::Display for VPD {
32    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
33        match self {
34            VPD::Y => write!(f, "Y"),
35            VPD::N => write!(f, "N"),
36        }
37    }
38}
39
40
41pub enum Param<'a> {
42    DateFrom { v: &'a String },
43    DateTo { v: &'a String },
44    CSVF { v: &'a CSVF },
45    UsingCodes { v: &'a String },
46    VPD { v: &'a VPD },
47    VFD { v: &'a String },
48}
49
50impl<'a> Param<'a> {
51    fn add_param_to_url(&self, url: &mut String) -> () {
52        let url_param: String = match self {
53            Param::DateFrom { v } => format!("&Datefrom={}", v),
54            Param::DateTo { v } => format!("&Dateto={}", v),
55            Param::CSVF { v } => format!("&CSVF={}", v.to_string()),
56            Param::UsingCodes { v } => format!("&UsingCodes={}", v),
57            Param::VPD { v } => format!("&VPD={}", v.to_string()),
58            Param::VFD { v } => format!("&VFD={}", v),
59        };
60        url.push_str(&url_param);
61    }
62}
63
64
65/// Make a request to the provided URL, validate the status code of the response, and return deserialized data.
66async fn process_request(url: String) -> Result<Vec<IADBDataPoint>, Box<dyn Error>> {
67    let user_agent: &str = "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.90 Safari/537.36";
68    let client: Client = Client::builder().user_agent(user_agent).build()?;
69    let response: Response = client.get(url).send().await?;
70    let response_body: String = response.text().await?;
71    // Deserialize the CSV
72    let mut rdr: Reader<&[u8]> = ReaderBuilder::new().has_headers(true).from_reader(response_body.as_bytes());
73    // Replace default CSV headers
74    let _: &StringRecord = rdr.headers()?;
75    rdr.set_headers(StringRecord::from(vec!["date", "value"]));
76    // Deserialize data
77    let mut data: Vec<IADBDataPoint> = Vec::<IADBDataPoint>::new();
78    for entry in rdr.deserialize() {
79        let entry: IADBDataPoint = entry?;
80        data.push(entry);
81    }
82    Ok(data)
83}
84
85
86/// Constructs a URL for API request, sends the request, and returns the deserialzied response.
87///
88/// # Input
89/// - `series_code`: Code of the time series in the IADB.
90/// - `params`: List of parameters expected by the IADB API endpoint
91/// - `additional_params`: Additional parameters to add to the request
92pub async fn call_api_endpoint<'a>(series_code: &String, params: Vec<Param<'a>>, additional_params: Option<String>) -> Result<IADBSeries, Box<dyn Error>> {
93    // Set up a URL for the API endpoint
94    let mut url: String = String::from(BASE_URL);
95    // Add parameters to the URL
96    url.push_str(&format!("?csv.x=yes&SeriesCodes={}", series_code));
97    for param in params {
98        param.add_param_to_url(&mut url);
99    }
100    // Add additional parameters to the URL
101    match additional_params {
102        Some(v) => url.push_str(&v),
103        None => (),
104    }
105    // Process API response
106    let data: Vec<IADBDataPoint> = process_request(url).await?;
107    Ok(IADBSeries { name: series_code.clone(), data, })
108}
109
110
111#[cfg(test)]
112mod tests {
113    use tokio;
114
115    #[tokio::test]
116    async fn unit_test_request() -> () {
117        use reqwest::{Client, Response};
118        let user_agent: &str = "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.90 Safari/537.36";
119        let client: Client = Client::builder().user_agent(user_agent).build().unwrap();
120        // Request
121        let url: String = String::from("http://www.bankofengland.co.uk/boeapps/iadb/fromshowcolumns.asp?csv.x=yes&Datefrom=01/Jan/2000&Dateto=01/Oct/2018&SeriesCodes=IUMBV34,IUMBV37,IUMBV42,IUMBV45&CSVF=TT&UsingCodes=Y&VPD=Y&VFD=N");
122        let response: Response = client.get(url).send().await.unwrap();
123        let response_body: String = response.text().await.unwrap();
124        println!("{}", response_body)
125    }
126}