用 Rust 做一個貨幣轉換 CLI 小工具

#rust
用 Rust 做一個貨幣轉換 CLI 小工具
五倍技術部
技術文章
用 Rust 做一個貨幣轉換 CLI 小工具

貨幣轉換是一個經常會用到的功能,例如:我們在某某網路商店上看到一個標示美金的商品,想知道台幣的價格,那麼這時候就會需要貨幣轉換的功能。

既然有需求,那麼就來用 Rust 實作一個貨幣轉換的程式吧!

貨幣轉換的流程

流程蠻簡單的,大概是這樣:

  • 選擇要轉換的基準貨幣
  • 輸入金額
  • 選擇要轉換的目標貨幣

貨幣轉換的資料如何取得?

既然要做貨幣轉換,那麼就需要有各國貨幣的資料,目前有蠻多 API 提供這些資料,例如:ExchangeratesCurrencyConverterApiExchangeRate-API 等等,不過大部分都是要收費的服務,這篇文章會使用 Fixer 來取得資料,因為有基本免費的方案可以使用,雖然還是有一些限制,例如只有 1000 次的 API 請求次數,不過對於開發者來說,應該是夠用了。

Fixer 方案

在 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 中,先將 dialoguerdotenvtokio 引入:

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(&currencies)
    .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(&currencies)
    .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 小工具。如果想瞭解更多的話,可以參考五倍學院的教學影片。