fn validate_and_build_result<'a>(
currency_code: &'a str,
integer_part: &'a str,
decimal_part: Option<&'a str>,
separator: &'a str,
is_positive: bool,
) -> Option<(&'a str, String)> {
if integer_part.contains(separator) {
let groups: Vec<&str> = integer_part.split(separator).collect();
if groups[0].is_empty()
|| groups[0].len() > 3
|| !groups[0].chars().all(|c| c.is_ascii_digit())
{
return None;
}
for group in groups.iter().skip(1) {
if group.len() != 3 || !group.chars().all(|c| c.is_ascii_digit()) {
return None;
}
}
let mut result = groups.join("");
if let Some(dec) = decimal_part {
if dec.is_empty() || !dec.chars().all(|c| c.is_ascii_digit()) {
return None;
}
result.push('.');
result.push_str(dec);
}
if !is_positive {
result.insert(0, '-');
}
Some((currency_code, result))
} else {
if !integer_part.chars().all(|c| c.is_ascii_digit()) {
return None;
}
let mut result = integer_part.to_string();
if let Some(dec) = decimal_part {
if dec.is_empty() || !dec.chars().all(|c| c.is_ascii_digit()) {
return None;
}
result.push('.');
result.push_str(dec);
}
if !is_positive {
result.insert(0, '-');
}
Some((currency_code, result))
}
}
pub fn parse_comma_thousands_separator(s: &str) -> Option<(&str, String)> {
let parts: Vec<&str> = s.split_whitespace().collect();
if parts.len() != 2 || parts[0].is_empty() || parts[1].is_empty() {
return None;
}
let currency_code = parts[0];
let amount_str = parts[1];
if currency_code.is_empty()
|| currency_code.len() > 15
|| !currency_code.chars().all(|c| c.is_ascii_alphabetic())
{
return None;
}
let decimal_parts: Vec<&str> = amount_str.split('.').collect();
if decimal_parts.len() > 2 {
return None;
}
let (integer_part, is_positive) = if let Some(neg_trimmed) = decimal_parts[0].strip_prefix("-")
{
(neg_trimmed, false)
} else {
(decimal_parts[0], true)
};
let decimal_part = if decimal_parts.len() == 2 {
Some(decimal_parts[1])
} else {
None
};
validate_and_build_result(currency_code, integer_part, decimal_part, ",", is_positive)
}
pub fn parse_dot_thousands_separator(s: &str) -> Option<(&str, String)> {
let parts: Vec<&str> = s.split_whitespace().collect();
if parts.len() != 2 || parts[0].is_empty() || parts[1].is_empty() {
return None;
}
let currency_code = parts[0];
let amount_str = parts[1];
if currency_code.is_empty()
|| currency_code.len() > 15
|| !currency_code.chars().all(|c| c.is_ascii_alphabetic())
{
return None;
}
let decimal_parts: Vec<&str> = amount_str.split(',').collect();
if decimal_parts.len() > 2 {
return None;
}
let (integer_part, is_positive) = if let Some(neg_trimmed) = decimal_parts[0].strip_prefix("-")
{
(neg_trimmed, false)
} else {
(decimal_parts[0], true)
};
let decimal_part = if decimal_parts.len() == 2 {
Some(decimal_parts[1])
} else {
None
};
validate_and_build_result(currency_code, integer_part, decimal_part, ".", is_positive)
}
pub fn parse_symbol_comma_thousands_separator<C: crate::Currency>(
s: &str,
) -> Option<(&str, String)> {
let (stripped_money, is_positive) = if let Some(trimmed) = s.strip_prefix('-') {
(trimmed, false)
} else {
(s, true)
};
let amount_str = stripped_money.strip_prefix(C::SYMBOL)?;
if amount_str.is_empty() {
return None;
}
let decimal_parts: Vec<&str> = amount_str.split('.').collect();
if decimal_parts.len() > 2 {
return None;
}
let (integer_part, decimal_part) = if decimal_parts.len() == 2 {
(decimal_parts[0], Some(decimal_parts[1]))
} else {
(decimal_parts[0], None)
};
validate_and_build_result(C::SYMBOL, integer_part, decimal_part, ",", is_positive)
}
pub fn parse_symbol_dot_thousands_separator<C: crate::Currency>(s: &str) -> Option<(&str, String)> {
let (stripped_money, is_positive) = if let Some(trimmed) = s.strip_prefix('-') {
(trimmed, false)
} else {
(s, true)
};
let amount_str = stripped_money.strip_prefix(C::SYMBOL)?;
if amount_str.is_empty() {
return None;
}
let decimal_parts: Vec<&str> = amount_str.split(',').collect();
if decimal_parts.len() > 2 {
return None;
}
let (integer_part, decimal_part) = if decimal_parts.len() == 2 {
(decimal_parts[0], Some(decimal_parts[1]))
} else {
(decimal_parts[0], None)
};
validate_and_build_result(C::SYMBOL, integer_part, decimal_part, ".", is_positive)
}
pub fn parse_code_locale_separator<C: crate::Currency>(s: &str) -> Option<(&str, String)> {
let parts: Vec<&str> = s.split_whitespace().collect();
if parts.len() != 2 || parts[0].is_empty() || parts[1].is_empty() {
return None;
}
let currency_code = parts[0];
let amount_str = parts[1];
if currency_code.is_empty()
|| currency_code.len() > 15
|| !currency_code.chars().all(|c| c.is_ascii_alphabetic())
{
return None;
}
let decimal_parts: Vec<&str> = amount_str.split(C::DECIMAL_SEPARATOR).collect();
if decimal_parts.len() > 2 {
return None;
}
let (integer_part, is_positive) = if let Some(neg_trimmed) = decimal_parts[0].strip_prefix("-")
{
(neg_trimmed, false)
} else {
(decimal_parts[0], true)
};
let decimal_part = if decimal_parts.len() == 2 {
Some(decimal_parts[1])
} else {
None
};
validate_and_build_result(
currency_code,
integer_part,
decimal_part,
C::THOUSAND_SEPARATOR,
is_positive,
)
}
pub fn parse_symbol_locale_separator<C: crate::Currency>(s: &str) -> Option<(&str, String)> {
let (stripped_money, is_positive) = if let Some(trimmed) = s.strip_prefix('-') {
(trimmed, false)
} else {
(s, true)
};
let amount_str = stripped_money.strip_prefix(C::SYMBOL)?;
if amount_str.is_empty() {
return None;
}
let decimal_parts: Vec<&str> = amount_str.split(C::DECIMAL_SEPARATOR).collect();
if decimal_parts.len() > 2 {
return None;
}
let (integer_part, decimal_part) = if decimal_parts.len() == 2 {
(decimal_parts[0], Some(decimal_parts[1]))
} else {
(decimal_parts[0], None)
};
validate_and_build_result(
C::SYMBOL,
integer_part,
decimal_part,
C::THOUSAND_SEPARATOR,
is_positive,
)
}