odin_palace 0.1.0

Parser and analyzer for bank statements in the 1CClientBankExchange format
Documentation
const FIO_TEMPLATES: &[&str] = &[
    "Иванов Иван Иванович",
    "Петров Петр Петрович",
    "Сидоров Сидор Сидорович",
    "Петрова Галина Леонидовна",
    "Кузнецова Мария Сергеевна",
    "Смирнов Алексей Алексеевич",
    "Васильева Ольга Николаевна",
    "Морозов Дмитрий Сергеевич",
    "Попова Наталья Владимировна",
    "Волков Андрей Владимирович",
];

const PURPOSE_TEMPLATES: &[&str] = &[
    "Оплата по договору",
    "Перевод средств",
    "Тестовая операция",
    "Услуги",
    "Платеж за услуги",
    "Погашение задолженности",
    "Возврат средств",
    "Тестовый платеж",
    "Авансовый платеж",
    "Прочие операции",
];

pub fn anonymize_str(input: &str) -> String {
    let mut fio_map = std::collections::HashMap::new();
    let mut purpose_map = std::collections::HashMap::new();
    let mut account_map = std::collections::HashMap::new();
    let mut id_map = std::collections::HashMap::new();
    let mut payer_num = 1;
    let mut payee_num = 1;
    let mut fio_idx = 0;
    let mut purpose_idx = 0;
    let mut account_idx = 1;
    let mut id_idx = 1;
    let mut lines = Vec::new();

    let payer_keys = ["Плательщик", "Плательщик1"];
    let payee_keys = ["Получатель", "Получатель1"];

    for line in input.split('\n') {
        if let Some((key, value)) = line.split_once('=') {
            let value = value.trim();
            if payer_keys.iter().any(|k| key.trim() == *k) && !fio_map.contains_key(value) {
                let templ = if fio_idx < FIO_TEMPLATES.len() {
                    FIO_TEMPLATES[fio_idx]
                } else {
                    let t = format!("Плательщик_{payer_num}");
                    payer_num += 1;
                    fio_map.insert(value.to_string(), t.clone());
                    fio_idx += 1;
                    continue;
                };
                fio_map.insert(value.to_string(), templ.to_string());
                fio_idx += 1;
            } else if payee_keys.iter().any(|k| key.trim() == *k) && !fio_map.contains_key(value) {
                let templ = if fio_idx < FIO_TEMPLATES.len() {
                    FIO_TEMPLATES[fio_idx]
                } else {
                    let t = format!("Получатель_{payee_num}");
                    payee_num += 1;
                    fio_map.insert(value.to_string(), t.clone());
                    fio_idx += 1;
                    continue;
                };
                fio_map.insert(value.to_string(), templ.to_string());
                fio_idx += 1;
            } else if key.contains("Счет")
                || key.contains("Корсчет") && !account_map.contains_key(value)
            {
                let prefix = &value[..std::cmp::min(5, value.len())];
                let num_len = value.len().saturating_sub(5);
                let new_num = format!("{account_idx:0num_len$}");
                let new_value = format!("{prefix}{new_num}");
                account_map.insert(value.to_string(), new_value);
                account_idx += 1;
            } else if key.contains("ИНН") || key.contains("КПП") && !id_map.contains_key(value)
            {
                let prefix = &value[..std::cmp::min(2, value.len())];
                let num_len = value.len().saturating_sub(2);
                let new_num = format!("{id_idx:0num_len$}");
                let new_value = format!("{prefix}{new_num}");
                id_map.insert(value.to_string(), new_value);
                id_idx += 1;
            } else if (key.to_lowercase().contains("purpose")
                || key.to_lowercase().contains("назначение")
                || key.to_lowercase().contains("description"))
                && !purpose_map.contains_key(value)
            {
                let templ = PURPOSE_TEMPLATES
                    .get(purpose_idx % PURPOSE_TEMPLATES.len())
                    .unwrap();
                purpose_map.insert(value.to_string(), templ.to_string());
                purpose_idx += 1;
            }
        }
        lines.push(line);
    }

    let mut out = String::with_capacity(input.len());
    for (i, line) in lines.iter().enumerate() {
        if let Some((key, value)) = line.split_once('=') {
            let value = value.trim();
            let new_value = if payer_keys.iter().any(|k| key.trim() == *k)
                || payee_keys.iter().any(|k| key.trim() == *k)
            {
                fio_map.get(value).map(|s| s.as_str()).unwrap_or(value)
            } else if key.contains("Счет") || key.contains("Корсчет") {
                account_map.get(value).map(|s| s.as_str()).unwrap_or(value)
            } else if key.contains("ИНН") || key.contains("КПП") {
                id_map.get(value).map(|s| s.as_str()).unwrap_or(value)
            } else if let Some(new) = purpose_map.get(value) {
                new
            } else {
                value
            };
            out.push_str(key);
            out.push('=');
            out.push_str(new_value);
        } else {
            out.push_str(line);
        }
        if i + 1 < lines.len() {
            out.push('\n');
        }
    }
    out
}