Replace exit function with panic, formatting
This commit is contained in:
parent
44b94ef042
commit
1245a6e2e9
69
src/cache.rs
69
src/cache.rs
|
@ -5,12 +5,11 @@ use std::{
|
|||
|
||||
use rusqlite::{Connection, Result};
|
||||
|
||||
use crate::config::{get_cache_path, println_and_exit};
|
||||
use crate::config::get_cache_path;
|
||||
|
||||
const CANNOT_CLOSE_MSG: &str = "Couldn't close sqlite connection";
|
||||
|
||||
pub fn check_code(code: &String) -> Result<bool>
|
||||
{
|
||||
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))",
|
||||
|
@ -22,18 +21,20 @@ pub fn check_code(code: &String) -> Result<bool>
|
|||
Ok(exists)
|
||||
}
|
||||
|
||||
pub fn list_currencies() -> Result<Vec<[String;2]>>
|
||||
{
|
||||
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})
|
||||
.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();
|
||||
|
||||
let mut result: Vec<[String; 2]> = Vec::new();
|
||||
for code in ret {
|
||||
let i = code.unwrap();
|
||||
let z = [i[0].clone(),i[1].clone()];
|
||||
let z = [i[0].clone(), i[1].clone()];
|
||||
result.push(z);
|
||||
}
|
||||
stmt.finalize()?;
|
||||
|
@ -41,20 +42,22 @@ pub fn list_currencies() -> Result<Vec<[String;2]>>
|
|||
Ok(result)
|
||||
}
|
||||
|
||||
|
||||
|
||||
pub fn list_rates(code_from: &String ) -> Result<Vec<[String;2]>>
|
||||
{
|
||||
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 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})
|
||||
.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();
|
||||
|
||||
let mut result: Vec<[String; 2]> = Vec::new();
|
||||
for code in ret {
|
||||
let i = code.unwrap();
|
||||
let z = [i[0].clone(),i[1].clone()];
|
||||
let z = [i[0].clone(), i[1].clone()];
|
||||
result.push(z);
|
||||
}
|
||||
stmt.finalize()?;
|
||||
|
@ -62,8 +65,7 @@ pub fn list_rates(code_from: &String ) -> Result<Vec<[String;2]>>
|
|||
Ok(result)
|
||||
}
|
||||
|
||||
pub fn check_exchange(code_from: &String, code_to: &String) -> Result<bool>
|
||||
{
|
||||
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
|
||||
|
@ -77,8 +79,7 @@ pub fn check_exchange(code_from: &String, code_to: &String) -> Result<bool>
|
|||
Ok(exists)
|
||||
}
|
||||
|
||||
pub fn get_rate(code_from: &String, code_to: &String) -> Result<String>
|
||||
{
|
||||
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
|
||||
|
@ -91,8 +92,7 @@ pub fn get_rate(code_from: &String, code_to: &String) -> Result<String>
|
|||
|
||||
Ok(rate)
|
||||
}
|
||||
pub fn get_next_update(code: &String) -> Result<u64>
|
||||
{
|
||||
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)",
|
||||
|
@ -108,8 +108,7 @@ pub fn add_rates(
|
|||
next_update: u64,
|
||||
code_from: &String,
|
||||
rates: &HashMap<String, serde_json::Value>,
|
||||
) -> Result<()>
|
||||
{
|
||||
) -> Result<()> {
|
||||
let conn = Connection::open(get_cache_path())?;
|
||||
|
||||
for (code_to, rate) in rates {
|
||||
|
@ -133,8 +132,7 @@ pub fn add_rates(
|
|||
Ok(())
|
||||
}
|
||||
|
||||
pub fn add_code(code: [String; 2]) -> Result<()>
|
||||
{
|
||||
pub fn add_code(code: [String; 2]) -> Result<()> {
|
||||
let conn = Connection::open(get_cache_path())?;
|
||||
conn.execute(
|
||||
"
|
||||
|
@ -146,8 +144,7 @@ pub fn add_code(code: [String; 2]) -> Result<()>
|
|||
conn.close().expect(CANNOT_CLOSE_MSG);
|
||||
Ok(())
|
||||
}
|
||||
pub fn get_api_key() -> Result<String>
|
||||
{
|
||||
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'",
|
||||
|
@ -158,8 +155,7 @@ pub fn get_api_key() -> Result<String>
|
|||
|
||||
Ok(api_key)
|
||||
}
|
||||
pub fn set_api_key(key: String) -> Result<()>
|
||||
{
|
||||
pub fn set_api_key(key: String) -> Result<()> {
|
||||
let conn = Connection::open(get_cache_path())?;
|
||||
conn.execute(
|
||||
"
|
||||
|
@ -174,11 +170,10 @@ pub fn set_api_key(key: String) -> Result<()>
|
|||
Ok(())
|
||||
}
|
||||
|
||||
pub fn create_cache() -> Result<()>
|
||||
{
|
||||
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")
|
||||
panic!("Specified path cache path is dir, not file")
|
||||
}
|
||||
if path.exists() {
|
||||
match remove_file(path) {
|
||||
|
@ -186,10 +181,10 @@ pub fn create_cache() -> Result<()>
|
|||
Err(_e) => match metadata(path) {
|
||||
Ok(md) => {
|
||||
if md.permissions().readonly() {
|
||||
println_and_exit("Can't modify file");
|
||||
panic!("Can't modify file");
|
||||
}
|
||||
}
|
||||
Err(_e) => println_and_exit("Unknown error while trying to remove old database"),
|
||||
Err(_e) => panic!("Unknown error while trying to remove old database"),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,8 +8,7 @@ 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
|
||||
{
|
||||
pub fn get_endpoint() -> String {
|
||||
let ret: String;
|
||||
match var_os(REST_ENDPOINT_ENV_NAME) {
|
||||
Some(val) => ret = val.to_str().unwrap().to_string(),
|
||||
|
@ -18,8 +17,7 @@ pub fn get_endpoint() -> String
|
|||
|
||||
ret
|
||||
}
|
||||
pub fn get_cache_path() -> PathBuf
|
||||
{
|
||||
pub fn get_cache_path() -> PathBuf {
|
||||
let mut path: PathBuf = PathBuf::new();
|
||||
match var_os(CACHE_LOCATION_ENV_NAME) {
|
||||
Some(val) => path.push(val),
|
||||
|
@ -31,15 +29,9 @@ pub fn get_cache_path() -> PathBuf
|
|||
|
||||
path
|
||||
}
|
||||
pub fn get_current_time() -> u64
|
||||
{
|
||||
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)
|
||||
}
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
use crate::*;
|
||||
use rust_decimal::prelude::*;
|
||||
use rusty_money::{iso::find, ExchangeRate, Money};
|
||||
pub async fn update_rate(code: &String)
|
||||
{
|
||||
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()
|
||||
{
|
||||
|
@ -10,25 +9,20 @@ pub async fn update_rate(code: &String)
|
|||
.await
|
||||
.expect("Error while fetching rates");
|
||||
if status == requests::Status::INVALID {
|
||||
config::println_and_exit("Invalid api key when getting rates")
|
||||
panic!("Invalid api key when getting rates")
|
||||
} else if status == requests::Status::LIMIT {
|
||||
config::println_and_exit("Exceeded API limit when getting rates")
|
||||
panic!("Exceeded API limit when getting rates")
|
||||
} else if status == requests::Status::ERROR {
|
||||
config::println_and_exit("Unknown error when getting rates")
|
||||
panic!("Unknown error when getting rates")
|
||||
}
|
||||
}
|
||||
}
|
||||
pub async fn get_rate(code_from: &String, code_to: &String) -> String
|
||||
{
|
||||
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(),
|
||||
)
|
||||
panic!("Code {} doesn't exists, use correct code!", code_from);
|
||||
}
|
||||
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(),
|
||||
)
|
||||
panic!("Code {} doesn't exists, use correct code!", code_to);
|
||||
}
|
||||
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")
|
||||
|
@ -39,19 +33,18 @@ pub async fn get_rate(code_from: &String, code_to: &String) -> String
|
|||
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)
|
||||
{
|
||||
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())
|
||||
panic!("{} is not a number!", value);
|
||||
}
|
||||
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())
|
||||
panic!("{} not found in ISO formats", code_from);
|
||||
}
|
||||
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())
|
||||
panic!("{} not found in ISO formats", code_to);
|
||||
}
|
||||
|
||||
let rate = Decimal::from_str(&text_rate).unwrap();
|
||||
|
|
24
src/main.rs
24
src/main.rs
|
@ -14,8 +14,7 @@ mod requests;
|
|||
|
||||
#[derive(Parser)]
|
||||
#[command(about, long_about = None, arg_required_else_help = true)]
|
||||
struct Cli
|
||||
{
|
||||
struct Cli {
|
||||
/// Currency code to exchange from
|
||||
#[arg(value_names = ["Currency input"])]
|
||||
currency_from: Option<String>,
|
||||
|
@ -44,8 +43,7 @@ struct Cli
|
|||
#[arg(short = 'L', long = "list-rates", value_names = ["currency"])]
|
||||
list_rates: Option<String>,
|
||||
}
|
||||
async fn setup_key(key: String) -> Result<bool, Box<dyn std::error::Error>>
|
||||
{
|
||||
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 {
|
||||
|
@ -65,8 +63,7 @@ async fn setup_key(key: String) -> Result<bool, Box<dyn std::error::Error>>
|
|||
}
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<(), Box<dyn std::error::Error>>
|
||||
{
|
||||
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let args = Cli::parse();
|
||||
|
||||
let all_args =
|
||||
|
@ -75,7 +72,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>>
|
|||
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")
|
||||
panic!("Do not provide codes and value with --interactive")
|
||||
}
|
||||
if args.recreate_cache || !config::get_cache_path().exists() {
|
||||
create_cache()?;
|
||||
|
@ -97,7 +94,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>>
|
|||
.len()
|
||||
> 0)
|
||||
{
|
||||
config::println_and_exit("API Key is not set up!");
|
||||
panic!("API Key is not set up!");
|
||||
}
|
||||
if args.list {
|
||||
let currencies = cache::list_currencies()?;
|
||||
|
@ -108,17 +105,15 @@ async fn main() -> Result<(), Box<dyn std::error::Error>>
|
|||
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());
|
||||
panic!("Code {} not found", code);
|
||||
}
|
||||
exchange::update_rate(&code).await;
|
||||
let rates = cache::list_rates(&code)?;
|
||||
for rate in rates {
|
||||
println!("{} to {} rate: {}", code ,rate[0], rate[1]);
|
||||
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'",
|
||||
);
|
||||
panic!("Not all args specified, provide 'currency from', 'currency to' and 'amount'");
|
||||
} else if all_args {
|
||||
convert_value(
|
||||
&args.currency_from.unwrap().to_uppercase(),
|
||||
|
@ -132,8 +127,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>>
|
|||
}
|
||||
Ok(())
|
||||
}
|
||||
async fn interactive() -> Result<(), Box<dyn std::error::Error>>
|
||||
{
|
||||
async fn interactive() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let mut key_setup = cache::get_api_key()
|
||||
.expect("Error while getting api key")
|
||||
.len()
|
||||
|
|
|
@ -4,8 +4,7 @@ use crate::cache::{self, get_api_key};
|
|||
use crate::config::get_endpoint;
|
||||
use serde::Deserialize;
|
||||
#[derive(PartialEq)]
|
||||
pub enum Status
|
||||
{
|
||||
pub enum Status {
|
||||
OK,
|
||||
INVALID,
|
||||
LIMIT,
|
||||
|
@ -13,13 +12,11 @@ pub enum Status
|
|||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct CurrencyCodes
|
||||
{
|
||||
struct CurrencyCodes {
|
||||
supported_codes: Vec<[String; 2]>,
|
||||
}
|
||||
#[derive(Deserialize)]
|
||||
struct ConversionRates
|
||||
{
|
||||
struct ConversionRates {
|
||||
base_code: String,
|
||||
time_next_update_unix: u64,
|
||||
|
||||
|
@ -27,14 +24,12 @@ struct ConversionRates
|
|||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct Err
|
||||
{
|
||||
struct Err {
|
||||
#[serde(rename = "error-type")]
|
||||
error_type: String,
|
||||
}
|
||||
|
||||
pub async fn get_rates(code: &String) -> Result<Status, reqwest::Error>
|
||||
{
|
||||
pub async fn get_rates(code: &String) -> Result<Status, reqwest::Error> {
|
||||
let response = reqwest::get(format!(
|
||||
"{}{}{}{}",
|
||||
get_endpoint(),
|
||||
|
@ -65,8 +60,7 @@ pub async fn get_rates(code: &String) -> Result<Status, reqwest::Error>
|
|||
|
||||
Ok(Status::ERROR)
|
||||
}
|
||||
pub async fn get_currencies() -> Result<Status, reqwest::Error>
|
||||
{
|
||||
pub async fn get_currencies() -> Result<Status, reqwest::Error> {
|
||||
let response = reqwest::get(format!(
|
||||
"{}{}{}",
|
||||
get_endpoint(),
|
||||
|
|
Reference in New Issue