mirror of
https://github.com/Horizontal42/summarum.git
synced 2026-11-11 02:12:00 +03:00
dedupe historical-rate map parsing
fetch_historical_rates and fetch_historical_rates_batch each built the same USD-anchored, uppercased rate map inline. Extract parse_rate_map and cover it with tests.
This commit is contained in:
parent
a8121dbc90
commit
2d682448c6
1 changed files with 47 additions and 19 deletions
|
|
@ -384,6 +384,23 @@ fn valid_date_str(date: &str) -> bool {
|
||||||
|
|
||||||
static HTTP_CLIENT: std::sync::OnceLock<reqwest::Client> = std::sync::OnceLock::new();
|
static HTTP_CLIENT: std::sync::OnceLock<reqwest::Client> = std::sync::OnceLock::new();
|
||||||
|
|
||||||
|
/// frankfurter quotes everything against the `from=USD` base, so USD itself is
|
||||||
|
/// never in the response and has to be anchored by hand.
|
||||||
|
fn parse_rate_map(
|
||||||
|
raw_rates: &serde_json::Map<String, serde_json::Value>,
|
||||||
|
) -> std::collections::HashMap<String, f64> {
|
||||||
|
let mut rates = std::collections::HashMap::new();
|
||||||
|
rates.insert("USD".to_string(), 1.0_f64);
|
||||||
|
for (code, val) in raw_rates {
|
||||||
|
if let Some(v) = val.as_f64() {
|
||||||
|
if v > 0.0 {
|
||||||
|
rates.insert(code.to_uppercase(), v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
rates
|
||||||
|
}
|
||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
async fn fetch_historical_rates(
|
async fn fetch_historical_rates(
|
||||||
app: AppHandle,
|
app: AppHandle,
|
||||||
|
|
@ -415,16 +432,7 @@ async fn fetch_historical_rates(
|
||||||
.json()
|
.json()
|
||||||
.await
|
.await
|
||||||
.map_err(|e| e.to_string())?;
|
.map_err(|e| e.to_string())?;
|
||||||
let raw_rates = body["rates"].as_object().ok_or("no rates in response")?;
|
let rates = parse_rate_map(body["rates"].as_object().ok_or("no rates in response")?);
|
||||||
let mut rates = std::collections::HashMap::new();
|
|
||||||
rates.insert("USD".to_string(), 1.0_f64);
|
|
||||||
for (code, val) in raw_rates {
|
|
||||||
if let Some(v) = val.as_f64() {
|
|
||||||
if v > 0.0 {
|
|
||||||
rates.insert(code.to_uppercase(), v);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if let Ok(serialized) = serde_json::to_string(&rates) {
|
if let Ok(serialized) = serde_json::to_string(&rates) {
|
||||||
write_atomic(&cache_path, &serialized).ok();
|
write_atomic(&cache_path, &serialized).ok();
|
||||||
}
|
}
|
||||||
|
|
@ -477,15 +485,7 @@ async fn fetch_historical_rates_batch(
|
||||||
if let Ok(resp) = client_clone.get(&url).send().await {
|
if let Ok(resp) = client_clone.get(&url).send().await {
|
||||||
if let Ok(body) = resp.json::<serde_json::Value>().await {
|
if let Ok(body) = resp.json::<serde_json::Value>().await {
|
||||||
if let Some(rates_obj) = body["rates"].as_object() {
|
if let Some(rates_obj) = body["rates"].as_object() {
|
||||||
let mut daily_rates = std::collections::HashMap::new();
|
let daily_rates = parse_rate_map(rates_obj);
|
||||||
daily_rates.insert("USD".to_string(), 1.0_f64);
|
|
||||||
for (code, val) in rates_obj {
|
|
||||||
if let Some(v) = val.as_f64() {
|
|
||||||
if v > 0.0 {
|
|
||||||
daily_rates.insert(code.to_uppercase(), v);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let cache_path = data_dir(&app_clone).join(format!("rates-{}.json", date));
|
let cache_path = data_dir(&app_clone).join(format!("rates-{}.json", date));
|
||||||
if let Ok(serialized) = serde_json::to_string(&daily_rates) {
|
if let Ok(serialized) = serde_json::to_string(&daily_rates) {
|
||||||
let _ = crate::write_atomic(&cache_path, &serialized);
|
let _ = crate::write_atomic(&cache_path, &serialized);
|
||||||
|
|
@ -990,4 +990,32 @@ mod tests {
|
||||||
assert!(!is_sheet_path("numi"));
|
assert!(!is_sheet_path("numi"));
|
||||||
assert!(!is_sheet_path(""));
|
assert!(!is_sheet_path(""));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// parse_rate_map
|
||||||
|
#[test]
|
||||||
|
fn parse_rate_map_always_anchors_usd() {
|
||||||
|
let obj = serde_json::json!({});
|
||||||
|
let map = parse_rate_map(obj.as_object().unwrap());
|
||||||
|
assert_eq!(map.get("USD"), Some(&1.0));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn parse_rate_map_uppercases_codes() {
|
||||||
|
let obj = serde_json::json!({ "eur": 0.9, "Gbp": 0.8 });
|
||||||
|
let map = parse_rate_map(obj.as_object().unwrap());
|
||||||
|
assert_eq!(map.get("EUR"), Some(&0.9));
|
||||||
|
assert_eq!(map.get("GBP"), Some(&0.8));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn parse_rate_map_skips_nonpositive_and_nonnumeric() {
|
||||||
|
let obj =
|
||||||
|
serde_json::json!({ "EUR": 0.9, "AAA": 0.0, "BBB": -1.5, "CCC": "oops", "DDD": null });
|
||||||
|
let map = parse_rate_map(obj.as_object().unwrap());
|
||||||
|
assert_eq!(map.get("EUR"), Some(&0.9));
|
||||||
|
assert!(!map.contains_key("AAA"));
|
||||||
|
assert!(!map.contains_key("BBB"));
|
||||||
|
assert!(!map.contains_key("CCC"));
|
||||||
|
assert!(!map.contains_key("DDD"));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue