polyio/
api_info.rs

1// Copyright (C) 2019-2020 Daniel Mueller <deso@posteo.net>
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4use std::env::var_os;
5use std::ffi::OsString;
6
7use url::Url;
8
9use crate::Error;
10
11/// The base URL to the API to use.
12const ENV_API_URL: &str = "POLYGON_API_URL";
13/// The base URL to the market data stream to use.
14const ENV_STREAM_URL: &str = "POLYGON_STREAM_URL";
15/// The environment variable representing the API key.
16const ENV_API_KEY: &str = "POLYGON_API_KEY";
17
18/// The default stream URL.
19const DEFAULT_API_URL: &str = "https://api.polygon.io";
20/// The default stream URL.
21const DEFAULT_STREAM_URL: &str = "wss://socket.polygon.io";
22
23
24/// An object encapsulating the information used for working with the
25/// Alpaca API.
26#[derive(Clone, Debug, PartialEq)]
27pub struct ApiInfo {
28  /// The base URL for API requests.
29  pub(crate) api_url: Url,
30  /// The base URL for market data streaming.
31  pub(crate) stream_url: Url,
32  /// The API key to use for authentication.
33  pub(crate) api_key: String,
34}
35
36impl ApiInfo {
37  /// Create an `ApiInfo` object using the given API key and assuming
38  /// default API and Stream endpoint URLs.
39  pub fn new<S>(api_key: S) -> Self
40  where
41    S: Into<String>,
42  {
43    Self {
44      api_url: Url::parse(DEFAULT_API_URL).unwrap(),
45      stream_url: Url::parse(DEFAULT_STREAM_URL).unwrap(),
46      api_key: api_key.into(),
47    }
48  }
49
50  /// Create an `ApiInfo` object with information from the environment.
51  ///
52  /// This constructor retrieves API related information from the
53  /// environment and performs some preliminary validation on it. The
54  /// following information is used:
55  /// - the Polygon API base URL is retrieved from the POLYGON_API_URL
56  ///   variable
57  /// - the Polygon streaming base URL is retrieved from the
58  ///   POLYGON_STREAM_URL variable
59  /// - the Polygon API key is retrieved from the POLYGON_API_KEY
60  ///   variable
61  pub fn from_env() -> Result<Self, Error> {
62    let api_url = var_os(ENV_API_URL)
63      .unwrap_or_else(|| OsString::from(DEFAULT_API_URL))
64      .into_string()
65      .map_err(|_| {
66        Error::Str(
67          format!(
68            "{} environment variable is not a valid string",
69            ENV_API_URL
70          )
71          .into(),
72        )
73      })?;
74    let api_url = Url::parse(&api_url)?;
75
76    let stream_url = var_os(ENV_STREAM_URL)
77      .unwrap_or_else(|| OsString::from(DEFAULT_STREAM_URL))
78      .into_string()
79      .map_err(|_| {
80        Error::Str(
81          format!(
82            "{} environment variable is not a valid string",
83            ENV_STREAM_URL
84          )
85          .into(),
86        )
87      })?;
88    let stream_url = Url::parse(&stream_url)?;
89
90    let api_key = var_os(ENV_API_KEY)
91      .ok_or_else(|| Error::Str(format!("{} environment variable not found", ENV_API_KEY).into()))?
92      .into_string()
93      .map_err(|_| {
94        Error::Str(format!("{} environment variable is not a valid string", ENV_API_KEY).into())
95      })?;
96
97    Ok(Self {
98      api_url,
99      stream_url,
100      api_key,
101    })
102  }
103}
104
105
106#[cfg(test)]
107mod tests {
108  use super::*;
109
110
111  /// Verify that we can create an `ApiInfo` object.
112  #[test]
113  fn create_api_info() {
114    // We mainly verify that the default URLs can be parsed without an
115    // error.
116    let _ = ApiInfo::new("XXXXXXXXXXXXXXXXXXXX");
117  }
118}