4.6 KiB
CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
專案性質
抓取台灣銀行 CSV 匯率並即時換算成台幣的跨平台 GUI 小工具。目標是「使用者雙擊即跑、零 runtime 依賴」,所以主程式是 Rust(bot_rates_rs/src/main.rs);根目錄下的 bot_rates.py / bot_rates_gui.py / build.bat 是已被取代的早期 Python 版本,修改時請以 Rust 版為準。
Android 版獨立在 android/(Kotlin + Jetpack Compose),用純 CLI(gradle + sdkmanager)建置,不依賴 Android Studio。核心邏輯(CSV col 13、TARGETS)與 Rust 版對齊,改動時兩邊要同步。詳細見 android/README.md。
常用指令
所有 cargo 指令都要在 bot_rates_rs/ 下執行,並帶 RUST_MIN_STACK=16777216(少了這個 LLVM 在 release 優化 eframe 依賴時會堆疊溢位 SIGSEGV)。
cd bot_rates_rs
# macOS 版 release build
RUST_MIN_STACK=16777216 cargo build --release
# 輸出:target/release/bot_rates
# Windows 版(從 macOS 交叉編譯,需先 brew install mingw-w64 並 rustup target add x86_64-pc-windows-gnu)
RUST_MIN_STACK=16777216 cargo build --release --target x86_64-pc-windows-gnu
# 輸出:target/x86_64-pc-windows-gnu/release/bot_rates.exe
# 快速 debug 跑一次(不用設 RUST_MIN_STACK)
cargo run
部署(複製編譯產物到專案根目錄的散佈位置):
# macOS:塞進 .app bundle 並清 quarantine
cp target/release/bot_rates ../BotRates.app/Contents/MacOS/BotRates
xattr -cr ../BotRates.app
# Windows:複製 exe 到根目錄並重新壓 zip
cp target/x86_64-pc-windows-gnu/release/bot_rates.exe ../bot_rates.exe
cd .. && zip -9 bot_rates.zip bot_rates.exe
專案目前沒有測試、沒有 lint 設定、沒有 CI。
架構要點
單一檔案 Rust app(~410 行 src/main.rs),關鍵分層:
- 資料抓取:
fetch_rates()用ureq+rustls(刻意避開reqwest/native-tls,因為交叉編譯會牽扯 OpenSSL / SChannel)抓https://rate.bot.com.tw/xrt/flcsv/0/day。CSV 解析用位置索引而非欄名,因為台銀 CSV 有重複欄名(買入/賣出區段都叫「匯率/現金/即期」),csv::DictReader式的解析會只保留最後一個。目前讀cols[13]即期賣出;買入在cols[3],現金買入cols[2]、現金賣出cols[12]。 - UI:
eframe/egui0.27 immediate mode。App::update每幀重繪;抓匯率走背景 thread +mpsc::Receiver,update用try_recv無阻塞輪詢,沒資料時request_repaint_after(100ms)自我喚醒。 - 輸入狀態:
App.amounts: HashMap<String, String>,存字串不是f64,這樣才能保留「100.」「空字串」「1,000」這類輸入中間態。parse_amount才做清洗與 parse。 - 字型:egui 預設字型沒中文,會變豆腐。
setup_fonts在執行期讀 OS 字型(Windowsmsjh.ttc/ macOSPingFang.ttc,有 fallback 鏈),刻意不 bundle 字型以免 binary 胖 5–15 MB。如果在沒有這些字型的環境跑就會回到豆腐。 - 時間:
now_string/civil_from_days自己實作 UTC→台北時間換算與 Gregorian 日期計算,避免引入chrono/time把 binary 撐大。 - 複製:「複製」按鈕用
ui.output_mut(|o| o.copied_text = raw)寫純數字(3162.00,不含NT$/ 逗號)到剪貼簿,設計是為了貼到 Excel 會被當數字。TWD label 另外有.selectable(true)讓使用者自行選取。
擴充時的常見改點
| 目標 | 位置 |
|---|---|
| 新增幣別 | TARGETS 常數,加一行 (代碼, 中文名) 即可,其他地方會自動處理 |
| 改成即期買入/現金匯率 | fetch_rates 裡的 cols[13] 換成對應 index(見上方資料抓取段) |
| 視窗尺寸/不可 resize | main() 的 ViewportBuilder |
| 預設輸入金額 | App::new 裡的 "100".to_string() |
| 配色 | update 開頭的 bg / card / rate_color / twd_color 等常數 |
Release profile 的硬性約束
Cargo.toml 的 [profile.release] 是 opt-level = 3, lto = "thin", codegen-units = 1, strip = true, panic = "abort"。歷史上試過 opt-level = "z" + lto = true 會觸發 LLVM DeadArgumentElimination 的 SIGSEGV,所以不要回頭改成更激進的設定。RUST_MIN_STACK=16777216 同樣是必備而非可選。
macOS .app bundle
BotRates.app/Contents/{Info.plist, MacOS/BotRates} 是手動組的;沒有 cargo-bundle 或腳本自動化。改版本/顯示名稱要直接編輯 Info.plist。首次散佈給使用者前跑 xattr -cr BotRates.app 避免 Gatekeeper 擋下。