pub(crate) fn parse_steam_balance(s: &str) -> Option<f64> {
let digits: String = s.chars().filter(|c| c.is_ascii_digit() || *c == '.' || *c == ',').collect();
if digits.is_empty() {
return None;
}
let has_dot = digits.contains('.');
let has_comma = digits.contains(',');
match (has_dot, has_comma) {
(false, false) => digits.parse::<f64>().ok(),
(true, true) => {
let last_sep = digits.chars().rev().find(|c| *c == '.' || *c == ',');
if last_sep == Some(',') {
digits.replace('.', "").replace(',', ".").parse::<f64>().ok()
} else {
digits.replace(',', "").parse::<f64>().ok()
}
}
(true, false) => {
let dot_count = digits.matches('.').count();
let after_last = digits.rsplit('.').next().unwrap_or("");
if dot_count > 1 || after_last.len() == 3 {
digits.replace('.', "").parse::<f64>().ok()
} else {
digits.parse::<f64>().ok()
}
}
(false, true) => {
let comma_count = digits.matches(',').count();
let after_last = digits.rsplit(',').next().unwrap_or("");
if comma_count > 1 || after_last.len() == 3 {
digits.replace(',', "").parse::<f64>().ok()
} else {
digits.replace(',', ".").parse::<f64>().ok()
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_with_decimal() {
assert_eq!(parse_steam_balance("35.016,48₫"), Some(35016.48));
}
#[test]
fn test_whole_number() {
assert_eq!(parse_steam_balance("132.500₫"), Some(132500.0));
}
#[test]
fn test_no_thousands() {
assert_eq!(parse_steam_balance("500₫"), Some(500.0));
}
#[test]
fn test_empty() {
assert_eq!(parse_steam_balance(""), None);
assert_eq!(parse_steam_balance("N/A"), None);
}
#[test]
fn us_decimal_no_thousands() {
assert_eq!(parse_steam_balance("12.34"), Some(12.34));
}
#[test]
fn us_decimal_with_thousands() {
assert_eq!(parse_steam_balance("1,234.56"), Some(1234.56));
}
#[test]
fn eu_decimal_no_thousands() {
assert_eq!(parse_steam_balance("12,34"), Some(12.34));
}
#[test]
fn eu_decimal_with_thousands() {
assert_eq!(parse_steam_balance("1.234,56"), Some(1234.56));
}
#[test]
fn plain_integer() {
assert_eq!(parse_steam_balance("1234"), Some(1234.0));
}
}