apca/api/v2/
assets.rs

1// Copyright (C) 2019-2024 The apca Developers
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4use serde::Deserialize;
5use serde::Serialize;
6use serde_urlencoded::to_string as to_query;
7
8use crate::api::v2::asset::Asset;
9use crate::api::v2::asset::Class;
10use crate::api::v2::asset::Status;
11use crate::Str;
12
13
14/// A GET request to be made to the /v2/assets endpoint.
15#[derive(Clone, Copy, Debug, Default, Deserialize, Eq, PartialEq, Serialize)]
16pub struct ListReq {
17  /// The status of assets to include in the response.
18  #[serde(rename = "status")]
19  pub status: Status,
20  /// The asset class of which to include assets in the response.
21  #[serde(rename = "asset_class")]
22  pub class: Class,
23  /// The type is non-exhaustive and open to extension.
24  #[doc(hidden)]
25  #[serde(skip)]
26  pub _non_exhaustive: (),
27}
28
29
30Endpoint! {
31  /// The representation of a GET request to the /v2/assets endpoint.
32  pub List(ListReq),
33  Ok => Vec<Asset>, [
34    /// The list of assets was retrieved successfully.
35    /* 200 */ OK,
36  ],
37  Err => ListError, []
38
39  #[inline]
40  fn path(_input: &Self::Input) -> Str {
41    "/v2/assets".into()
42  }
43
44  fn query(input: &Self::Input) -> Result<Option<Str>, Self::ConversionError> {
45    Ok(Some(to_query(input)?.into()))
46  }
47}
48
49
50#[cfg(test)]
51mod tests {
52  use super::*;
53
54  use serde_json::from_slice as from_json;
55  use serde_json::to_vec as to_json;
56
57  use test_log::test;
58
59  use crate::api::v2::asset::Exchange;
60  use crate::api_info::ApiInfo;
61  use crate::Client;
62
63
64  /// Check that we can serialize and deserialize a [`ListReq`].
65  #[test]
66  fn serialize_deserialize_list_request() {
67    let request = ListReq {
68      status: Status::Active,
69      class: Class::UsEquity,
70      ..Default::default()
71    };
72
73    let json = to_json(&request).unwrap();
74    assert_eq!(from_json::<ListReq>(&json).unwrap(), request);
75  }
76
77
78  /// Make sure that we can list available US stock assets.
79  #[test(tokio::test)]
80  async fn list_us_stock_assets() {
81    let api_info = ApiInfo::from_env().unwrap();
82    let client = Client::new(api_info);
83    let request = ListReq::default();
84    let assets = client.issue::<List>(&request).await.unwrap();
85
86    let asset = assets.iter().find(|x| x.symbol == "AAPL").unwrap();
87    assert_eq!(asset.class, Class::UsEquity);
88    assert_eq!(asset.exchange, Exchange::Nasdaq);
89    assert_eq!(asset.status, Status::Active);
90  }
91
92
93  /// Make sure that we can list available crypto currency assets.
94  #[test(tokio::test)]
95  async fn list_crypto_assets() {
96    let api_info = ApiInfo::from_env().unwrap();
97    let client = Client::new(api_info);
98    let request = ListReq {
99      class: Class::Crypto,
100      ..Default::default()
101    };
102
103    let assets = client.issue::<List>(&request).await.unwrap();
104
105    let asset = assets.iter().find(|x| x.symbol == "BTC/USD").unwrap();
106    assert_eq!(asset.class, Class::Crypto);
107  }
108}