Initial commit
This commit is contained in:
commit
4efafdd85b
|
@ -0,0 +1 @@
|
|||
/target
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,17 @@
|
|||
[package]
|
||||
name = "currency-exchange"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
clap = { version = "4.5.2", features = [ "derive" ] }
|
||||
reqwest = "0.11.25"
|
||||
rusqlite = { version = "0.31.0", features = ["bundled", "array"] }
|
||||
rusty-money = "0.4.1"
|
||||
serde = { version ="1.0.197", features = ["derive"] }
|
||||
serde_json = "1.0.114"
|
||||
tokio = { version = "1.36.0", features = ["full"] }
|
||||
rust_decimal = "1.34"
|
||||
rust_decimal_macros = "1.34"
|
|
@ -0,0 +1 @@
|
|||
brace_style="AlwaysNextLine"
|
|
@ -0,0 +1,240 @@
|
|||
use std::{
|
||||
collections::HashMap,
|
||||
fs::{metadata, remove_file},
|
||||
};
|
||||
|
||||
use rusqlite::{Connection, Result};
|
||||
|
||||
use crate::config::{get_cache_path, println_and_exit};
|
||||
|
||||
const CANNOT_CLOSE_MSG: &str = "Couldn't close sqlite connection";
|
||||
|
||||
pub fn check_code(code: &String) -> Result<bool>
|
||||
{
|
||||
let conn = Connection::open(get_cache_path())?;
|
||||
let exists: bool = conn.query_row(
|
||||
"SELECT EXISTS(SELECT code FROM currencies WHERE currencies.code = UPPER($1))",
|
||||
[code],
|
||||
|row| row.get(0),
|
||||
)?;
|
||||
conn.close().expect(CANNOT_CLOSE_MSG);
|
||||
|
||||
Ok(exists)
|
||||
}
|
||||
|
||||
pub fn list_currencies() -> Result<Vec<[String;2]>>
|
||||
{
|
||||
let conn = Connection::open(get_cache_path())?;
|
||||
let mut stmt = conn.prepare("SELECT code, text FROM currencies ORDER BY code")?;
|
||||
let ret = stmt
|
||||
.query_map([], |row| {let v:Result<[String;2]> = Ok([row.get(0)?,row.get(1)?]); v})
|
||||
.expect("Error while listing currencies");
|
||||
|
||||
let mut result: Vec<[String;2]> = Vec::new();
|
||||
for code in ret {
|
||||
let i = code.unwrap();
|
||||
let z = [i[0].clone(),i[1].clone()];
|
||||
result.push(z);
|
||||
}
|
||||
stmt.finalize()?;
|
||||
conn.close().expect(CANNOT_CLOSE_MSG);
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
|
||||
|
||||
pub fn list_rates(code_from: &String ) -> Result<Vec<[String;2]>>
|
||||
{
|
||||
let conn = Connection::open(get_cache_path())?;
|
||||
let mut stmt = conn.prepare("SELECT code_to, rate FROM exchange_rates WHERE code_from = $1 ORDER BY code_to")?;
|
||||
let ret = stmt
|
||||
.query_map([code_from], |row| {let v:Result<[String;2]> = Ok([row.get(0)?,row.get(1)?]); v})
|
||||
.expect("Error while listing rates");
|
||||
|
||||
let mut result: Vec<[String;2]> = Vec::new();
|
||||
for code in ret {
|
||||
let i = code.unwrap();
|
||||
let z = [i[0].clone(),i[1].clone()];
|
||||
result.push(z);
|
||||
}
|
||||
stmt.finalize()?;
|
||||
conn.close().expect(CANNOT_CLOSE_MSG);
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
pub fn check_exchange(code_from: &String, code_to: &String) -> Result<bool>
|
||||
{
|
||||
let conn = Connection::open(get_cache_path())?;
|
||||
let exists: bool = conn.query_row(
|
||||
"SELECT EXISTS(SELECT code_from, code_to
|
||||
FROM exchange_rates
|
||||
WHERE exchange_rates.code_from = UPPER($1) AND exchange_rates.code_to = UPPER($2))",
|
||||
[code_from, code_to],
|
||||
|row| row.get(0),
|
||||
)?;
|
||||
conn.close().expect(CANNOT_CLOSE_MSG);
|
||||
|
||||
Ok(exists)
|
||||
}
|
||||
|
||||
pub fn get_rate(code_from: &String, code_to: &String) -> Result<String>
|
||||
{
|
||||
let conn = Connection::open(get_cache_path())?;
|
||||
let rate: String = conn.query_row(
|
||||
"SELECT rate
|
||||
FROM exchange_rates
|
||||
WHERE exchange_rates.code_from = UPPER($1) AND exchange_rates.code_to = UPPER($2)",
|
||||
[code_from, code_to],
|
||||
|row| row.get(0),
|
||||
)?;
|
||||
conn.close().expect(CANNOT_CLOSE_MSG);
|
||||
|
||||
Ok(rate)
|
||||
}
|
||||
pub fn get_next_update(code: &String) -> Result<u64>
|
||||
{
|
||||
let conn = Connection::open(get_cache_path())?;
|
||||
let next_update: u64 = conn.query_row(
|
||||
"SELECT next_update FROM currencies WHERE currencies.code = UPPER($1)",
|
||||
[code],
|
||||
|row| row.get(0),
|
||||
)?;
|
||||
conn.close().expect(CANNOT_CLOSE_MSG);
|
||||
|
||||
Ok(next_update)
|
||||
}
|
||||
|
||||
pub fn add_rates(
|
||||
next_update: u64,
|
||||
code_from: &String,
|
||||
rates: &HashMap<String, serde_json::Value>,
|
||||
) -> Result<()>
|
||||
{
|
||||
let conn = Connection::open(get_cache_path())?;
|
||||
|
||||
for (code_to, rate) in rates {
|
||||
conn.execute(
|
||||
"
|
||||
INSERT OR REPLACE INTO exchange_rates(code_from,code_to,rate)
|
||||
VALUES(UPPER($1),UPPER($2),$3)
|
||||
",
|
||||
[code_from, code_to, &rate.to_string()],
|
||||
)?;
|
||||
}
|
||||
conn.execute(
|
||||
"
|
||||
UPDATE currencies
|
||||
SET next_update = $1
|
||||
WHERE currencies.code = UPPER($2)
|
||||
",
|
||||
[&next_update.to_string(), code_from],
|
||||
)?;
|
||||
conn.close().expect(CANNOT_CLOSE_MSG);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn add_code(code: [String; 2]) -> Result<()>
|
||||
{
|
||||
let conn = Connection::open(get_cache_path())?;
|
||||
conn.execute(
|
||||
"
|
||||
INSERT OR IGNORE INTO currencies(code,text,next_update)
|
||||
VALUES(UPPER($1),$2,0)
|
||||
",
|
||||
[code.get(0), code.get(1)],
|
||||
)?;
|
||||
conn.close().expect(CANNOT_CLOSE_MSG);
|
||||
Ok(())
|
||||
}
|
||||
pub fn get_api_key() -> Result<String>
|
||||
{
|
||||
let conn = Connection::open(get_cache_path())?;
|
||||
let api_key: String = conn.query_row(
|
||||
"SELECT value FROM config WHERE config.name = 'API_KEY'",
|
||||
[],
|
||||
|row| row.get(0),
|
||||
)?;
|
||||
conn.close().expect(CANNOT_CLOSE_MSG);
|
||||
|
||||
Ok(api_key)
|
||||
}
|
||||
pub fn set_api_key(key: String) -> Result<()>
|
||||
{
|
||||
let conn = Connection::open(get_cache_path())?;
|
||||
conn.execute(
|
||||
"
|
||||
UPDATE config
|
||||
SET value = $1
|
||||
WHERE config.name = 'API_KEY'
|
||||
",
|
||||
[key],
|
||||
)?;
|
||||
conn.close().expect(CANNOT_CLOSE_MSG);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn create_cache() -> Result<()>
|
||||
{
|
||||
let path = &get_cache_path();
|
||||
if path.is_dir() {
|
||||
println_and_exit("Specified path cache path is dir, not file")
|
||||
}
|
||||
if path.exists() {
|
||||
match remove_file(path) {
|
||||
Ok(()) => (),
|
||||
Err(_e) => match metadata(path) {
|
||||
Ok(md) => {
|
||||
if md.permissions().readonly() {
|
||||
println_and_exit("Can't modify file");
|
||||
}
|
||||
}
|
||||
Err(_e) => println_and_exit("Unknown error while trying to remove old database"),
|
||||
},
|
||||
}
|
||||
}
|
||||
let conn = Connection::open(path)?;
|
||||
|
||||
conn.execute(
|
||||
"
|
||||
CREATE TABLE config (
|
||||
name TEXT PRIMARY KEY,
|
||||
value TEXT NOT NULL
|
||||
)",
|
||||
(),
|
||||
)?;
|
||||
|
||||
conn.execute(
|
||||
"
|
||||
CREATE TABLE currencies (
|
||||
code TEXT PRIMARY KEY,
|
||||
text TEXT NOT NULL,
|
||||
next_update TIME NOT NULL
|
||||
)",
|
||||
(),
|
||||
)?;
|
||||
|
||||
conn.execute(
|
||||
"
|
||||
CREATE TABLE exchange_rates (
|
||||
code_from TEXT NOT NULL,
|
||||
code_to TEXT NOT NULL,
|
||||
rate TEXT NOT NULL,
|
||||
PRIMARY KEY (code_from, code_to)
|
||||
)",
|
||||
(),
|
||||
)?;
|
||||
|
||||
conn.execute(
|
||||
"
|
||||
INSERT INTO config (name, value) VALUES (
|
||||
'API_KEY',
|
||||
''
|
||||
)
|
||||
",
|
||||
(),
|
||||
)?;
|
||||
conn.close().expect(CANNOT_CLOSE_MSG);
|
||||
|
||||
Ok(())
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
use std::{
|
||||
env::{temp_dir, var_os},
|
||||
path::PathBuf,
|
||||
process::exit,
|
||||
};
|
||||
|
||||
pub const CACHE_LOCATION_ENV_NAME: &str = "CURRENCY_CACHE";
|
||||
pub const REST_ENDPOINT: &str = "https://v6.exchangerate-api.com/v6/";
|
||||
pub const REST_ENDPOINT_ENV_NAME: &str = "CURRENCY_ENDPOINT";
|
||||
|
||||
pub fn get_endpoint() -> String
|
||||
{
|
||||
let ret: String;
|
||||
match var_os(REST_ENDPOINT_ENV_NAME) {
|
||||
Some(val) => ret = val.to_str().unwrap().to_string(),
|
||||
None => ret = REST_ENDPOINT.to_string(),
|
||||
}
|
||||
|
||||
ret
|
||||
}
|
||||
pub fn get_cache_path() -> PathBuf
|
||||
{
|
||||
let mut path: PathBuf = PathBuf::new();
|
||||
match var_os(CACHE_LOCATION_ENV_NAME) {
|
||||
Some(val) => path.push(val),
|
||||
None => {
|
||||
path.push(temp_dir());
|
||||
path.push("currencyCache.db");
|
||||
}
|
||||
}
|
||||
|
||||
path
|
||||
}
|
||||
pub fn get_current_time() -> u64
|
||||
{
|
||||
std::time::SystemTime::now()
|
||||
.duration_since(std::time::UNIX_EPOCH)
|
||||
.unwrap_or_default()
|
||||
.as_secs()
|
||||
}
|
||||
pub fn println_and_exit(msg: &str)
|
||||
{
|
||||
println!("{}", msg);
|
||||
exit(1)
|
||||
}
|
|
@ -0,0 +1,69 @@
|
|||
use crate::*;
|
||||
use rust_decimal::prelude::*;
|
||||
use rusty_money::{iso::find, ExchangeRate, Money};
|
||||
pub async fn update_rate(code: &String)
|
||||
{
|
||||
if cache::get_next_update(code).expect("Error getting next update time from cache")
|
||||
<= config::get_current_time()
|
||||
{
|
||||
let status = requests::get_rates(code)
|
||||
.await
|
||||
.expect("Error while fetching rates");
|
||||
if status == requests::Status::INVALID {
|
||||
config::println_and_exit("Invalid api key when getting rates")
|
||||
} else if status == requests::Status::LIMIT {
|
||||
config::println_and_exit("Exceeded API limit when getting rates")
|
||||
} else if status == requests::Status::ERROR {
|
||||
config::println_and_exit("Unknown error when getting rates")
|
||||
}
|
||||
}
|
||||
}
|
||||
pub async fn get_rate(code_from: &String, code_to: &String) -> String
|
||||
{
|
||||
if !cache::check_code(code_from).expect("Error on getting code status") {
|
||||
config::println_and_exit(
|
||||
format!("Code {} doesn't exists, use correct code!", code_from).as_str(),
|
||||
)
|
||||
}
|
||||
if !cache::check_code(code_to).expect("Error on getting code status") {
|
||||
config::println_and_exit(
|
||||
format!("Code {} doesn't exists, use correct code!", code_to).as_str(),
|
||||
)
|
||||
}
|
||||
if (!cache::check_exchange(code_from, code_to).expect("Error on getting exchange status"))
|
||||
|| (cache::get_next_update(code_from).expect("Error getting next update time from cache")
|
||||
<= config::get_current_time())
|
||||
{
|
||||
update_rate(code_from).await;
|
||||
}
|
||||
cache::get_rate(code_from, code_to).expect("Error when getting cached rate")
|
||||
}
|
||||
|
||||
pub async fn convert_value(code_from: &String, code_to: &String, value: &String)
|
||||
{
|
||||
if value.parse::<f64>().is_err() {
|
||||
config::println_and_exit(format!("{} is not a number!", value).as_str())
|
||||
}
|
||||
let text_rate = get_rate(code_from, code_to).await;
|
||||
let from_currency = find(code_from);
|
||||
if from_currency.is_none() {
|
||||
config::println_and_exit(format!("{} not found in ISO formats", code_from).as_str())
|
||||
}
|
||||
let to_currency = find(code_to);
|
||||
if to_currency.is_none() {
|
||||
config::println_and_exit(format!("{} not found in ISO formats", code_to).as_str())
|
||||
}
|
||||
|
||||
let rate = Decimal::from_str(&text_rate).unwrap();
|
||||
let dec_amount = Decimal::from_str(&value).unwrap();
|
||||
let from_money = Money::from_decimal(dec_amount, from_currency.unwrap());
|
||||
println!("Input: {}", from_money.to_string());
|
||||
if code_from != code_to {
|
||||
let ex = ExchangeRate::new(from_currency.unwrap(), to_currency.unwrap(), rate).unwrap();
|
||||
let result = ex.convert(from_money).expect("Error while conversion");
|
||||
println!("Equals: {}", result.to_string())
|
||||
} else {
|
||||
println!("{}", from_money.to_string())
|
||||
}
|
||||
println!("Exchange rate: {}", text_rate);
|
||||
}
|
|
@ -0,0 +1,206 @@
|
|||
use std::io::Write;
|
||||
|
||||
use crate::{
|
||||
cache::{create_cache, set_api_key},
|
||||
requests::get_currencies,
|
||||
};
|
||||
use cache::check_code;
|
||||
use clap::Parser;
|
||||
use exchange::convert_value;
|
||||
mod cache;
|
||||
mod config;
|
||||
mod exchange;
|
||||
mod requests;
|
||||
|
||||
#[derive(Parser)]
|
||||
#[command(about, long_about = None, arg_required_else_help = true)]
|
||||
struct Cli
|
||||
{
|
||||
/// Currency code to exchange from
|
||||
currency_from: Option<String>,
|
||||
/// Currency code to exchange to
|
||||
currency_to: Option<String>,
|
||||
/// Currency amount to exchange
|
||||
value: Option<String>,
|
||||
|
||||
/// Set api key
|
||||
#[arg(short = 'k', long = "set-api-key")]
|
||||
api_key: Option<String>,
|
||||
/// Recrate cache
|
||||
#[arg(short = 'r', long = "recreate-cache")]
|
||||
recreate_cache: bool,
|
||||
/// Interactive mode
|
||||
#[arg(short, long)]
|
||||
interactive: bool,
|
||||
|
||||
/// List currencies
|
||||
#[arg(short, long)]
|
||||
list: bool,
|
||||
|
||||
/// List currencies
|
||||
#[arg(short = 'L', long = "list-rates")]
|
||||
list_rates: Option<String>,
|
||||
}
|
||||
async fn setup_key(key: String) -> Result<bool, Box<dyn std::error::Error>>
|
||||
{
|
||||
set_api_key(key)?;
|
||||
let status = get_currencies().await?;
|
||||
if status == requests::Status::INVALID {
|
||||
set_api_key("".to_string())?;
|
||||
println!("Api Key is invalid");
|
||||
return Ok(false);
|
||||
} else if status == requests::Status::LIMIT {
|
||||
set_api_key("".to_string())?;
|
||||
println!("Can't set up API key due to exceeded API limit");
|
||||
return Ok(false);
|
||||
} else if status == requests::Status::ERROR {
|
||||
set_api_key("".to_string())?;
|
||||
println!("Can't set up API key due to unknown error");
|
||||
return Ok(false);
|
||||
}
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<(), Box<dyn std::error::Error>>
|
||||
{
|
||||
let args = Cli::parse();
|
||||
|
||||
let all_args =
|
||||
args.currency_from.is_some() && args.currency_to.is_some() && args.value.is_some();
|
||||
let wrong_args =
|
||||
args.currency_from.is_some() && (args.currency_to.is_none() || args.value.is_none());
|
||||
|
||||
if args.interactive && (all_args || wrong_args) {
|
||||
config::println_and_exit("Do not provide codes and value with --interactive")
|
||||
}
|
||||
if args.recreate_cache || !config::get_cache_path().exists() {
|
||||
create_cache()?;
|
||||
}
|
||||
match args.api_key {
|
||||
None => {}
|
||||
Some(key) => {
|
||||
let res = setup_key(key)
|
||||
.await
|
||||
.expect("Unknown error while setting up key");
|
||||
if !res {
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
}
|
||||
if !args.interactive {
|
||||
if !(cache::get_api_key()
|
||||
.expect("Error while getting api key")
|
||||
.len()
|
||||
> 0)
|
||||
{
|
||||
config::println_and_exit("API Key is not set up!");
|
||||
}
|
||||
if args.list {
|
||||
let currencies = cache::list_currencies()?;
|
||||
for currency in currencies {
|
||||
println!("{} - {}", currency[0], currency[1]);
|
||||
}
|
||||
} else if args.list_rates.is_some() {
|
||||
let code = args.list_rates.unwrap().clone();
|
||||
let check = check_code(&code)?;
|
||||
if !check {
|
||||
config::println_and_exit(format!("Code {} not found", code).as_str());
|
||||
}
|
||||
exchange::update_rate(&code).await;
|
||||
let rates = cache::list_rates(&code)?;
|
||||
for rate in rates {
|
||||
println!("{} to {} rate: {}", code ,rate[0], rate[1]);
|
||||
}
|
||||
} else if wrong_args {
|
||||
config::println_and_exit(
|
||||
"Not all args specified, provide 'currency from', 'currency to' and 'amount'",
|
||||
);
|
||||
} else if all_args {
|
||||
convert_value(
|
||||
&args.currency_from.unwrap().to_uppercase(),
|
||||
&args.currency_to.unwrap().to_uppercase(),
|
||||
&args.value.unwrap(),
|
||||
)
|
||||
.await
|
||||
}
|
||||
} else {
|
||||
interactive().await?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
async fn interactive() -> Result<(), Box<dyn std::error::Error>>
|
||||
{
|
||||
let mut key_setup = cache::get_api_key()
|
||||
.expect("Error while getting api key")
|
||||
.len()
|
||||
> 0;
|
||||
while !key_setup {
|
||||
let mut key_string = String::new();
|
||||
print!("Please enter API Key: ");
|
||||
std::io::stdout().flush()?;
|
||||
std::io::stdin()
|
||||
.read_line(&mut key_string)
|
||||
.expect("Did not enter a correct string");
|
||||
setup_key(key_string.trim().to_string())
|
||||
.await
|
||||
.expect("Unknown error while setting up key");
|
||||
key_setup = cache::get_api_key()
|
||||
.expect("Error while getting api key")
|
||||
.len()
|
||||
> 0;
|
||||
}
|
||||
|
||||
let mut code_from: String = String::new();
|
||||
let mut code_to: String = String::new();
|
||||
let mut amount: String = String::new();
|
||||
|
||||
let mut code_from_check = false;
|
||||
let mut code_to_check = false;
|
||||
let mut amount_check = false;
|
||||
|
||||
while !code_from_check {
|
||||
code_from = String::new();
|
||||
print!("Please enter code of input currency: ");
|
||||
std::io::stdout().flush()?;
|
||||
std::io::stdin()
|
||||
.read_line(&mut code_from)
|
||||
.expect("Did not enter a correct string");
|
||||
code_from = code_from.trim().to_uppercase().to_string();
|
||||
code_from_check = cache::check_code(&code_from)?;
|
||||
if !code_from_check {
|
||||
println!("Code {} is unknown", code_from);
|
||||
}
|
||||
}
|
||||
while !code_to_check {
|
||||
code_to = String::new();
|
||||
print!("Please enter code of output currency: ");
|
||||
std::io::stdout().flush()?;
|
||||
std::io::stdin()
|
||||
.read_line(&mut code_to)
|
||||
.expect("Did not enter a correct string");
|
||||
code_to = code_to.trim().to_uppercase().to_string();
|
||||
code_to_check = cache::check_code(&code_to)?;
|
||||
if !code_to_check {
|
||||
println!("Code {} is unknown", code_to);
|
||||
}
|
||||
}
|
||||
|
||||
while !amount_check {
|
||||
amount = String::new();
|
||||
print!("Please enter amount of input currency: ");
|
||||
std::io::stdout().flush()?;
|
||||
std::io::stdin()
|
||||
.read_line(&mut amount)
|
||||
.expect("Did not enter a correct string");
|
||||
amount = amount.trim().to_string();
|
||||
if amount.parse::<f64>().is_err() {
|
||||
println!("{} is not a number!", amount)
|
||||
} else {
|
||||
amount_check = true
|
||||
}
|
||||
}
|
||||
convert_value(&code_from, &code_to, &amount).await;
|
||||
|
||||
Ok(())
|
||||
}
|
|
@ -0,0 +1,95 @@
|
|||
use std::collections::HashMap;
|
||||
|
||||
use crate::cache::{self, get_api_key};
|
||||
use crate::config::get_endpoint;
|
||||
use serde::Deserialize;
|
||||
#[derive(PartialEq)]
|
||||
pub enum Status
|
||||
{
|
||||
OK,
|
||||
INVALID,
|
||||
LIMIT,
|
||||
ERROR,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct CurrencyCodes
|
||||
{
|
||||
supported_codes: Vec<[String; 2]>,
|
||||
}
|
||||
#[derive(Deserialize)]
|
||||
struct ConversionRates
|
||||
{
|
||||
base_code: String,
|
||||
time_next_update_unix: u64,
|
||||
|
||||
conversion_rates: HashMap<String, serde_json::Value>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct Err
|
||||
{
|
||||
#[serde(rename = "error-type")]
|
||||
error_type: String,
|
||||
}
|
||||
|
||||
pub async fn get_rates(code: &String) -> Result<Status, reqwest::Error>
|
||||
{
|
||||
let response = reqwest::get(format!(
|
||||
"{}{}{}{}",
|
||||
get_endpoint(),
|
||||
get_api_key().expect("Error when getting api key from cache"),
|
||||
"/latest/",
|
||||
code.to_uppercase()
|
||||
))
|
||||
.await?;
|
||||
if response.status().is_success() {
|
||||
let response: ConversionRates =
|
||||
serde_json::from_str(&response.text().await?).expect("Error when deserializng");
|
||||
cache::add_rates(
|
||||
response.time_next_update_unix,
|
||||
&response.base_code,
|
||||
&response.conversion_rates,
|
||||
)
|
||||
.expect("Error while caching response");
|
||||
return Ok(Status::OK);
|
||||
} else {
|
||||
let err: Err =
|
||||
serde_json::from_str(&response.text().await?).expect("Error when deserializng");
|
||||
if err.error_type == "invalid-key" {
|
||||
return Ok(Status::INVALID);
|
||||
} else if err.error_type == "quota-reached" {
|
||||
return Ok(Status::LIMIT);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(Status::ERROR)
|
||||
}
|
||||
pub async fn get_currencies() -> Result<Status, reqwest::Error>
|
||||
{
|
||||
let response = reqwest::get(format!(
|
||||
"{}{}{}",
|
||||
get_endpoint(),
|
||||
get_api_key().expect("Error when getting api key from cache"),
|
||||
"/codes"
|
||||
))
|
||||
.await?;
|
||||
if response.status().is_success() {
|
||||
let codes: CurrencyCodes =
|
||||
serde_json::from_str(&response.text().await?).expect("Error when deserializng");
|
||||
for code in codes.supported_codes {
|
||||
cache::add_code(code).expect("Error when adding code to cache");
|
||||
}
|
||||
return Ok(Status::OK);
|
||||
} else {
|
||||
let err: Err =
|
||||
serde_json::from_str(&response.text().await?).expect("Error when deserializng");
|
||||
if err.error_type == "invalid-key" {
|
||||
return Ok(Status::INVALID);
|
||||
} else if err.error_type == "quota-reached" {
|
||||
return Ok(Status::LIMIT);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(Status::ERROR)
|
||||
}
|
Reference in New Issue