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
152
153
154
155
156
157
158
159
160
//! Module for returning latest price and volume information
//!
//! A lightweight alternative to the time series APIs, this service returns the
//! latest price and volume information for a security of your choice.
//!
//! You can read about [Quote][quote] API and what it returns
//! on alphavantage documentation
//!
//! [quote]: https://www.alphavantage.co/documentation/#latestprice

use crate::user::APIKey;
use serde_derive::Deserialize;

/// Struct for storing Quote related information
#[derive(Debug, Deserialize)]
pub struct Quote {
    #[serde(rename = "Error Message")]
    error_message: Option<String>,
    #[serde(rename = "Information")]
    information: Option<String>,
    #[serde(rename = "Global Quote")]
    global_quote: Option<GlobalQuote>,
}

/// Struct storing Global Quote Value
#[derive(Debug, Deserialize, Clone)]
struct GlobalQuote {
    #[serde(rename = "01. symbol")]
    symbol: String,
    #[serde(rename = "02. open")]
    open: String,
    #[serde(rename = "03. high")]
    high: String,
    #[serde(rename = "04. low")]
    low: String,
    #[serde(rename = "05. price")]
    price: String,
    #[serde(rename = "06. volume")]
    volume: String,
    #[serde(rename = "07. latest trading day")]
    last_day: String,
    #[serde(rename = "08. previous close")]
    previous_close: String,
    #[serde(rename = "09. change")]
    change: String,
    #[serde(rename = "10. change percent")]
    change_percent: String,
}

impl Quote {
    /// return open value
    pub fn open(&self) -> Result<f64, String> {
        self.return_f64_value("open")
    }

    /// return high value
    pub fn high(&self) -> Result<f64, String> {
        self.return_f64_value("high")
    }

    /// return low value
    pub fn low(&self) -> Result<f64, String> {
        self.return_f64_value("low")
    }

    /// return price value
    pub fn price(&self) -> Result<f64, String> {
        self.return_f64_value("price")
    }

    /// return previous
    pub fn previous(&self) -> Result<f64, String> {
        self.return_f64_value("previous")
    }

    /// return change
    pub fn change(&self) -> Result<f64, String> {
        self.return_f64_value("change")
    }

    /// return change percent
    pub fn change_percent(&self) -> Result<f64, String> {
        let previous = self.previous()?;
        let price = self.price()?;
        Ok((price - previous) / previous)
    }

    /// general function used for returning f64 value of Quote method
    fn return_f64_value(&self, value: &str) -> Result<f64, String> {
        if let Some(global) = &self.global_quote {
            let price = match value {
                "open" => &global.open,
                "high" => &global.high,
                "low" => &global.low,
                "price" => &global.price,
                "previous" => &global.previous_close,
                "change" => &global.change,
                _ => "",
            };
            return Ok(price.trim().parse::<f64>().unwrap());
        } else if let Some(error) = &self.error_message {
            Err(format!("Error Message : {}", error))
        } else {
            Err(format!(
                "Information : {}",
                self.information.clone().unwrap()
            ))
        }
    }

    /// get last trading day
    pub fn last_trading(&self) -> Result<String, String> {
        self.return_string_value("trading")
    }

    /// get symbol
    ///
    /// ```
    /// let api = alpha_vantage::set_api("demo");
    /// let quote = api.quote("MSFT");
    /// let symbol = quote.symbol();
    /// assert_eq!(symbol.unwrap(), "MSFT");
    /// ```
    pub fn symbol(&self) -> Result<String, String> {
        self.return_string_value("symbol")
    }

    /// general function used for returning String value
    fn return_string_value(&self, value: &str) -> Result<String, String> {
        if let Some(global) = &self.global_quote {
            let value = match value {
                "trading" => global.last_day.to_string(),
                "symbol" => global.symbol.to_string(),
                _ => "".to_string(),
            };
            return Ok(value);
        } else if let Some(error) = &self.error_message {
            Err(format!("Error Message : {}", error))
        } else {
            Err(format!(
                "Information : {}",
                self.information.clone().unwrap()
            ))
        }
    }
}

/// Function used to create a [Quote][Quote] struct.
///
/// Instead of using this function directly calling through [APIKey][APIKey]
/// method is recommended
pub fn quote(symbol: &str, api_data: (&str, Option<u64>)) -> Quote {
    let api;
    if let Some(timeout) = api_data.1 {
        api = APIKey::set_with_timeout(api_data.0, timeout);
    } else {
        api = APIKey::set_api(api_data.0);
    }
    api.quote(symbol)
}