ccdata_api/
utils.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
use std::io::{Error, ErrorKind};
use reqwest::{Error as RequestError, Response};
use serde::de::DeserializeOwned;
use crate::{CCUnit, CCAPIEndpoint};
use crate::schemas::data_api::spot::CCSpotInstrumentStatus;
use crate::schemas::data_api::news::{CCNewsLang, CCNewsSourceID, CCNewsSourceType, CCNewsStatus};


/// Returns the value inside the Option or an Error with the message.
pub fn some_or_error<T>(input: Option<T>, message: &str) -> Result<T, Error> {
    match input {
        Some(v) => Ok(v),
        None => Err(Error::new(ErrorKind::InvalidData, message))
    }
}


/// Market interface that ensures all market enums implement .to_string().
pub trait Market {
    fn to_string(&self) -> String;
}


/// Convert a vector of inputs into a string of inputs suitable for use in REST API requests.
fn vec_to_str(vec: &Vec<String>) -> String {
    let mut s: String = String::from("");
    for v in vec {
        s.push_str(&v);
        s.push_str(&",");
    }
    s.pop();
    s
}


/// Converts a vector of optional parameters into a string.
fn optional_vec_arguments(o: Option<Vec<String>>, api_argument: String) -> String {
    let mut s: String = String::from("");
    match o {
        Some(v) => {
            s.push_str(&api_argument);
            s.push_str(&vec_to_str(&v));
        },
        None => (),
    }
    s
}


/// All possible parameter types for the REST API request.
pub enum Param<'a> {
    // Instrument parameters
    Symbol { v: &'a String },
    Instrument { v: &'a String },
    Instruments { v: &'a Vec<String> },
    ChainAsset { v: &'a String },
    Asset {v: &'a String},
    // Overlapping parameters
    ToTs {v: Option<i64>},
    ToTimestamp { v: Option<i64> },
    Limit { v: Option<usize> },
    // Special parameters
    Market {v: String},
    InstrumentStatus { v: CCSpotInstrumentStatus },
    OCCoreBlockNumber { v: i64 },
    OCCoreAddress { v: &'a String },
    OCCoreQuoteAsset { v: &'a String },
    NewsLanguage { v: CCNewsLang },
    NewsSourceID { v: CCNewsSourceID },
    NewsCategories { v: Option<Vec<String>> },
    NewsExcludeCategories { v: Option<Vec<String>> },
    NewsSourceType { v: CCNewsSourceType },
    NewsStatus { v: CCNewsStatus },
}

impl<'a> Param<'a> {
    fn add_param_to_url(&self, url: &mut String) -> () {
        let url_param: String = match self {
            // Instrument parameters
            Param::Symbol { v } => format!("&fsym={}", v),
            Param::Instrument { v } => format!("&instrument={}", v),
            Param::Instruments { v } => format!("&instruments={}", vec_to_str(v.to_owned())),
            Param::ChainAsset { v } => format!("&chain_asset={}", v),
            Param::Asset { v } => format!("&asset={}", v),
            // Overlapping parameters
            Param::ToTs { v } => match v {
                Some(v_) => format!("&toTs={}", v_),
                None => String::from(""),
            },
            Param::ToTimestamp { v } => match v {
                Some(v_) => format!("&to_ts={}", v_),
                None => String::from(""),
            },
            Param::Limit { v } => match v {
                Some(v_) => format!("&limit={}", v_),
                None => String::from("&limit=2000"),
            },
            // Special parameters
            Param::Market { v } => format!("&market={}", v),
            Param::InstrumentStatus { v } => format!("&instrument_status={}", v.to_string()),
            Param::OCCoreBlockNumber { v } => format!("&block_number={}", v),
            Param::OCCoreAddress { v } => format!("&address={}", v),
            Param::OCCoreQuoteAsset { v } => format!("&quote_asset={}", v),
            Param::NewsLanguage { v } => format!("&lang={}", v.to_string()),
            Param::NewsSourceID { v } => format!("&source_ids={}", v.to_string()),
            Param::NewsCategories { v } => optional_vec_arguments(v.to_owned(), String::from("&categories=")),
            Param::NewsExcludeCategories { v } => optional_vec_arguments(v.to_owned(), String::from("&exclude_categories=")),
            Param::NewsSourceType { v } => format!("&source_type={}", v.to_string()),
            Param::NewsStatus { v } => format!("&status={}", v.to_string()),
        };
        url.push_str(&url_param);
    }
}


/// Converts reqwest::Error to std::io::Error.
fn request_err_to_io_err<T>(res: Result<T, RequestError>) -> Result<T, Error> {
    match res {
        Ok(r) => Ok(r),
        Err(e) => Err(Error::new(ErrorKind::Other, e.without_url().to_string()))
    }
}


/// Make a request to the provided URL, validate the status code of the response, and return deserialized data.
async fn process_request<T: DeserializeOwned>(url: String) -> Result<T, Error> {
    let response: Response = request_err_to_io_err(reqwest::get(url).await)?;
    let response_body: String = request_err_to_io_err(response.text().await)?;
    let data: T = serde_json::from_str(&response_body)?;
    Ok(data)
}


/// Constructs a URL for API request, sends the request, and returns the deserialzied response.
pub async fn call_api_endpoint<'a, R: DeserializeOwned>(api_key: &String, endpoint: CCAPIEndpoint, unit: CCUnit, params: Vec<Param<'a>>,
                                                        additional_params: Option<String>) -> Result<R, Error> {
    // Set up a URL for the API endpoint
    let mut url: String = endpoint.url(&unit);
    // Add parameters to the URL
    url.push_str(&format!("?api_key={}", api_key));
    for param in params {
        param.add_param_to_url(&mut url);
    }
    // Add additional parameters to the URL
    match additional_params {
        Some(v) => url.push_str(&v),
        None => (),
    }
    // Process API response
    process_request::<R>(url).await
}