currency_prices/lib.rs
1mod api_client;
2pub mod models;
3
4/// A small library for fetching cryptocurrency prices using the CoinMarketCap API.
5///
6/// The library includes a `CurrencyPrices` struct that requires a `CoinMarketCapConfig`.
7/// You can obtain the development configuration by calling `CoinMarketCapConfig::get_sandbox_config()`,
8/// and for the production configuration, use `CoinMarketCapConfig::get_production_config()`.
9///
10/// # Example
11///
12/// ```rust
13/// use currency_prices::{
14/// models::{CoinMarketCapConfig, Currency},
15/// CurrencyPrices,
16/// };
17///
18/// let config = CoinMarketCapConfig::get_sandbox_config();
19/// let prices = CurrencyPrices::new(config);
20/// ```
21///
22/// The library exposes a struct containing a function for fetching cryptocurrency prices.
23/// You need to pass the `from_currency` and `to_currency` as arguments.
24///
25/// The `get_price` method is asynchronous, returning a `CurrencyPrice` object.
26///
27/// # Example
28///
29/// ```rust
30/// use currency_prices::{
31/// models::{CoinMarketCapConfig, Currency},
32/// CurrencyPrices,
33/// };
34///
35///
36/// #[tokio::main]
37/// async fn main() {
38/// let api_key = String::from("my_api_key");
39/// let config = CoinMarketCapConfig::get_production_config(api_key);
40/// let prices = CurrencyPrices::new(config);
41///
42/// let from_currency = Currency {
43/// name: String::from("HDN"),
44/// };
45/// let to_currency = Currency {
46/// name: String::from("USD"),
47/// };
48///
49/// let price = prices.get_price(from_currency, to_currency).await;
50///
51/// match price {
52/// Ok(currency_price) => {
53/// println!("Price from {} to {} is: {}", currency_price.from_currency.name, currency_price.to_currency.name, currency_price.price);
54/// }
55/// Err(err) => {
56/// eprintln!("Error fetching price: {:#?}", err);
57/// }
58/// }
59/// }
60/// ```
61///
62/// ## Struct: CurrencyPrice
63/// A structure representing cryptocurrency price information.
64///
65/// ```rust
66/// use currency_prices::{
67/// models::{CoinMarketCapConfig, Currency},
68/// CurrencyPrices,
69/// };
70/// use rust_decimal::prelude::*;
71///
72/// pub struct CurrencyPrice {
73/// pub from_currency: Currency,
74/// pub to_currency: Currency,
75/// pub price: Decimal,
76/// }
77/// ```
78use models::{CoinMarketCapConfig, Currency, CurrencyPrice, CurrencyPricesError};
79
80pub struct CurrencyPrices {
81 coin_market_cap_config: CoinMarketCapConfig,
82}
83
84impl CurrencyPrices {
85 pub fn new(coin_market_cap_config: CoinMarketCapConfig) -> CurrencyPrices {
86 Self {
87 coin_market_cap_config,
88 }
89 }
90
91 pub async fn get_price(
92 &self,
93 from_currency: Currency,
94 to_currency: Currency,
95 ) -> Result<CurrencyPrice, CurrencyPricesError> {
96 let api_response =
97 api_client::send_request(&from_currency, &to_currency, &self.coin_market_cap_config)
98 .await;
99
100 match api_response {
101 Ok(api_response) => {
102 let currency_price_maybe =
103 api_response
104 .data
105 .get(&to_currency.name)
106 .and_then(|data_item| {
107 data_item
108 .quote
109 .get(&from_currency.name)
110 .map(|quote| quote.price)
111 });
112
113 currency_price_maybe
114 .map(|price| {
115 Ok(CurrencyPrice {
116 from_currency,
117 to_currency,
118 price,
119 })
120 })
121 .unwrap_or(Err(CurrencyPricesError::Custom(String::from(
122 "The response didn't have the requested currency",
123 ))))
124 }
125
126 Err(reqwuest_error) => Err(CurrencyPricesError::Request(reqwuest_error)),
127 }
128 }
129}