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
//! Generic functions and structs for the API
//!
//! This module contains the generic functions and structs for the API.
//! These are used by the `Client`, and public for the whole crate

use chrono::prelude::*;
use std::fmt::Debug;

use reqwest::StatusCode;
use serde::de::DeserializeOwned;
use serde::{Deserialize, Serialize};

use crate::error::{Error, LemonError};

/// Module for interacting with the account related endpoints
mod market_data;
mod orders;
mod trading;

/// Generic struct for Endpoints that returns pagination information alongside data
///
/// Properties:
///
/// * `time`: The time the request was made.
/// * `status`: The status of the request.
/// * `mode`: The mode of the request.
/// * `results`: The actual results of the query.
/// * `previous`: The URL of the previous page of results.
/// * `next`: The URL for the next page of results.
/// * `total`: The total number of results available.
/// * `page`: The current page number
/// * `pages`: The total number of pages in the response.
#[derive(Deserialize, Serialize, Debug)]
pub struct PaginationResponse<T> {
    /// The time the request was made.
    pub time: DateTime<Utc>,
    /// The status of the request.
    pub status: Option<String>,
    /// The mode of the request. Can be paper, live, or market_data
    pub mode: Option<Mode>,
    /// The actual results of the query. Depends upon the given generics
    pub results: Option<Vec<T>>,
    /// The URL of the previous page of results.
    pub previous: Option<String>,
    /// The URL for the next page of results.
    pub next: Option<String>,
    /// The total number of results available.
    pub total: i64,
    /// The current page number
    pub page: i64,
    /// The total number of pages in the response.
    pub pages: i64,
}

/// General response struct
#[derive(Deserialize, Debug)]
pub struct Response {
    /// Timestamp of your request
    pub time: String,
    /// Environment the request was placed in: "paper" or "money"
    // TODO: Make this an enum
    pub mode: Mode,
    /// Status of the request. Returns 'ok' if successful
    pub status: String,
}

/// Generic response struct
#[derive(Deserialize, Debug)]
pub struct GenericResponse<T> {
    /// Timestamp of your request
    pub time: DateTime<Utc>,
    /// Environment the request was placed in: "paper" or "money"
    // TODO: Make this an enum
    pub mode: Mode,
    /// Status of the request.
    pub status: String,
    /// The actual results of the query. Depends upon the given generics
    pub results: Option<T>,
}

/// Trading mode
#[derive(Deserialize, Serialize, Debug)]
#[serde(rename_all = "lowercase")]
pub enum Mode {
    /// Paper trading mode
    Paper,
    /// Live trading mode
    Live,
    /// Market data mode
    MarketData,
}

impl std::fmt::Display for Mode {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        match *self {
            Mode::Paper => write!(f, "paper"),
            Mode::Live => write!(f, "live"),
            Mode::MarketData => write!(f, "market_data"),
        }
    }
}

/// Traits with API methods that are common across all calls
pub(crate) trait Requests {
    /// Generic get request
    fn get<T: DeserializeOwned + Debug>(&self, path: &str) -> Result<T, Error>;
    /// Generic get request with query parameters
    fn get_with_query<T: DeserializeOwned + Debug, Q: IntoIterator + Serialize>(
        &self,
        path: &str,
        query: Q,
    ) -> Result<T, Error>;

    /// Generic post request
    fn post<T: DeserializeOwned, B: Serialize>(&self, path: &str, body: B) -> Result<T, Error>;

    /// Generic delete request
    fn delete<T: DeserializeOwned>(&self, path: &str, path_param: &str) -> Result<T, Error>;

    /// Crate wide function to handle responses and errors
    fn response_handler<T: DeserializeOwned>(
        &self,
        response: reqwest::blocking::Response,
    ) -> Result<T, Error> {
        match response.status() {
            StatusCode::OK => Ok(response.json::<T>()?),
            _s => {
                let message = response.json::<LemonError>()?;
                Err(Error::Str(message.to_string()))
            }
        }
    }
    /// Crate wide function to handle query params
    fn get_query_string<Q: Serialize>(query: Q, query_vector: &mut Vec<String>) {
        if let Ok(query_string) = serde_urlencoded::to_string(&query) {
            query_vector.push(query_string);
        }
    }
}