#![deny(unsafe_code)]
#![warn(missing_docs)]
pub mod bic;
pub mod camt053;
pub mod camt054;
pub mod creditor_id;
pub mod iban;
pub mod pain001;
pub mod pain002;
pub mod pain008;
mod xml_util;
pub use bic::{Bic, BicError, validate_bic};
pub use camt053::{
BalanceType, Camt053Document, Camt053Entry, Camt053ParseError, Camt053Statement, EntryStatus,
StatementBalance, parse_camt053,
};
pub use camt054::{Camt054Entry, CreditDebitIndicator, ReturnInfo, UnknownIndicator};
pub use creditor_id::{CreditorId, CreditorIdError, validate_creditor_id};
pub use iban::{Iban, IbanError, validate_iban};
pub use pain001::{CreditTransferEntry, CreditTransferSchema, LocalInstrument, Pain001Builder};
pub use pain002::{
OriginalMessageType, Pain002Document, Pain002ParseError, PaymentInfoStatus, PaymentStatus,
ReasonCode, TransactionStatus, parse_pain002,
};
pub use pain008::{
DirectDebitEntry, DirectDebitScheme, Pain008Builder, SequenceType, UnknownSequenceType,
};
#[inline]
#[must_use]
pub fn ct_to_eur_str(ct: i64) -> String {
let sign = if ct < 0 { "-" } else { "" };
let abs = ct.unsigned_abs();
format!("{sign}{}.{:02}", abs / 100, abs % 100)
}
#[inline]
#[must_use]
pub fn ct_from_eur_str(s: &str) -> Option<i64> {
let trimmed = s.trim();
if trimmed.is_empty() {
return None;
}
let (sign, digits) = if let Some(rest) = trimmed.strip_prefix('-') {
(-1i64, rest)
} else {
(1i64, trimmed)
};
let ct: i64 = if let Some(dot) = digits.find('.') {
let euro_str = &digits[..dot];
let frac_str = &digits[dot + 1..];
let euros: i64 = if euro_str.is_empty() {
0
} else {
euro_str.parse().ok()?
};
let cents: i64 = match frac_str.len() {
0 => 0,
1 => frac_str.parse::<i64>().ok()?.checked_mul(10)?,
_ => frac_str[..2].parse().ok()?,
};
euros.checked_mul(100)?.checked_add(cents)?
} else {
digits.parse::<i64>().ok()?.checked_mul(100)?
};
ct.checked_mul(sign)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn ct_to_eur_roundtrip() {
for ct in [0i64, 1, 99, 100, 1234, 10_000, i64::MAX / 100] {
let s = ct_to_eur_str(ct);
assert_eq!(ct_from_eur_str(&s), Some(ct));
}
}
#[test]
fn ct_from_eur_negatives() {
assert_eq!(ct_from_eur_str("-5.00"), Some(-500));
assert_eq!(ct_from_eur_str("-0.01"), Some(-1));
}
#[test]
fn ct_from_eur_integer() {
assert_eq!(ct_from_eur_str("100"), Some(10_000));
}
#[test]
fn ct_from_eur_invalid() {
assert_eq!(ct_from_eur_str(""), None);
assert_eq!(ct_from_eur_str("abc"), None);
assert_eq!(ct_from_eur_str("1.2.3"), None);
}
}