貨幣轉換是一個經常會用到的功能,例如:我們在某某網路商店上看到一個標示美金的商品,想知道台幣的價格,那麼這時候就會需要貨幣轉換的功能。
既然有需求,那麼就來用 Rust 實作一個貨幣轉換的程式吧!
貨幣轉換的流程
流程蠻簡單的,大概是這樣:
- 選擇要轉換的基準貨幣
- 輸入金額
- 選擇要轉換的目標貨幣
貨幣轉換的資料如何取得?
既然要做貨幣轉換,那麼就需要有各國貨幣的資料,目前有蠻多 API 提供這些資料,例如:Exchangerates、CurrencyConverterApi、ExchangeRate-API 等等,不過大部分都是要收費的服務,這篇文章會使用 Fixer 來取得資料,因為有基本免費的方案可以使用,雖然還是有一些限制,例如只有 1000 次的 API 請求次數,不過對於開發者來說,應該是夠用了。
在 Fixer 註冊完取得 API Key 之後,就可以開始動手了!
建立專案
首先,先建立一個專案:
$ cargo new currency-converter
這個專案會安裝以下的套件:
[dependencies]
dialoguer = "0.11.0"
dotenv = "0.15.0"
reqwest = { version = "0.11.22", features = ["json"] }
serde_json = "1.0.107"
structopt = "0.3.26"
tokio = "1.32.0"
dialoguer
:像對話般的處理命令列。dotenv
:讀取.env
。reqwest
:發送 HTTP 請求。serde_json
:轉換 JSON 格式。structopt
:處理命令列參數。tokio
:處理非同步。
設定 API Key
在專案的根目錄新增一個 .env
檔案,並且在裡面新增一個 API_KEY
變數,並且將剛剛取得的 API Key 填入:
API_KEY=YOUR_API_KEY
實作 CLI
在 src/main.rs
中,先將 dialoguer
、dotenv
、tokio
引入:
use dotenv::dotenv;
use std::env;
use tokio::runtime::Builder;
並建立一個 get_conversion_rate
的函式,用來取得轉換後的值:
async fn get_conversion_rate() -> Result<(), Box<dyn std::error::Error>> {
Ok(())
}
設定貨幣 List
這個專案因為會讓使用者選擇貨幣,所以要先設定常用貨幣的 List:
async fn get_conversion_rate() -> Result<(), Box<dyn std::error::Error>> {
let currencies = ["TWD", "USD", "EUR", "JPY", "AUD", "KRW", "HKD"];
Ok(())
}
設定基準貨幣
接下來要讓使用者選擇基準貨幣,會使用 dialoguer
來處理:
let base_currency_index = dialoguer::Select::new()
.items(¤cies)
.default(0)
.interact()?;
let base_currency = currencies[base_currency_index];
這一段意思是讓使用者選擇貨幣,並且預設選擇第一個貨幣,並且將選擇的貨幣存到 base_currency
變數中。
讓使用者輸入金額
接下來要讓使用者輸入金額,這邊也是使用 dialoguer
來處理:
let amount: f64 = dialoguer::Input::new().with_prompt("請輸入金額").interact()?;
建立一個變數,預設型別為 f64
,並且提示使用者輸入金額,並且將輸入的值存到 amount
變數中。
設定目標貨幣
接下來要讓使用者選擇目標貨幣:
let target_currency_index = dialoguer::Select::new()
.items(¤cies)
.default(0)
.interact()?;
let target_currency = currencies[target_currency_index];
這一段跟設定基準貨幣的方式一樣,只是這邊是讓使用者選擇目標貨幣。
在 get_conversion_rate
函式最後面加上印出結果看看:
println!(
"{} {} {}",
amount, base_currency, target_currency
);
Ok(())
執行程式
接下來要在 main
函式中,執行 get_conversion_rate
函式,不過在這之前,要先使用 tokio
的 runtime builder:
use tokio::runtime::Builder;
fn main() {
let rt = Builder::new_current_thread()
.enable_all()
.build()
.unwrap();
rt.block_on(async {
match get_conversion_rate().await {
Ok(()) => (),
Err(e) => eprintln!("Error: {}", e),
}
});
}
這邊使用 Builder
來建立一個 runtime,並且使用 block_on
來執行 get_conversion_rate
函式。
執行的結果如下:
取得 API 資料
接下來要取得 API 資料:
dotenv().ok();
let api_key = env::var("API_KEY").expect("API_KEY must be set");
let url = format!(
"http://data.fixer.io/api/latest?access_key={}&symbols={},{}",
api_key, base_currency, target_currency
);
let response: serde_json::Value = reqwest::get(&url).await?.json().await?;
從 .env
中取得 API Key,並且組合 URL,最後使用 reqwest
來發送 HTTP 請求,並且將回傳的資料轉換成 JSON 格式。
接下來要取得轉換後的值:
let eur_to_target = if let Some(rate) = response["rates"][&target_currency].as_f64() {
rate
} else {
return Err("Target error".into());
};
let eur_to_base = if let Some(rate) = response["rates"][&base_currency].as_f64() {
rate
} else {
return Err("Base error".into());
};
let conversion_rate = eur_to_target / eur_to_base;
let converted_amount = amount * conversion_rate;
由於 Fixer API 免費的版本,回傳值只能以歐元為基準,所以要先取得歐元對目標貨幣的匯率,以及歐元對基準貨幣的匯率,最後再將兩者相除,就可以得到轉換後的匯率,並且將轉換後的匯率乘上使用者輸入的金額,就可以得到轉換後的值。
最後修改 println
的部分,並在 converted_amount
處理小數點前兩位:
println!(
"{} {} = {:.2} {}",
amount, base_currency, converted_amount, target_currency
);
最後執行的結果:
以上就是用 Rust 實作一個貨幣轉換的 CLI 小工具。如果想瞭解更多的話,可以參考五倍學院的教學影片。